diff --git a/deps/akihabara-core-1.3.1/GPL-LICENSE.txt b/deps/akihabara-core-1.3.1/GPL-LICENSE.txt
new file mode 100644
index 0000000..11dddd0
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/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/deps/akihabara-core-1.3.1/MIT-LICENSE.txt b/deps/akihabara-core-1.3.1/MIT-LICENSE.txt
new file mode 100644
index 0000000..9e82c0c
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/MIT-LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2010 Francesco Cottone, http://www.kesiev.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/deps/akihabara-core-1.3.1/README-AUDIO.markdown b/deps/akihabara-core-1.3.1/README-AUDIO.markdown
new file mode 100644
index 0000000..b59adb4
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/README-AUDIO.markdown
@@ -0,0 +1,7 @@
+Akihabara Audio
+===============
+
+* Make sure that you're using the latest version of your browser before using audio features.
+* Firefox stable has little audio caching problem/slowdowns and sometime freezes - can't figure out if is plugin's fault. Seems fixed on nightly builds. Audio is not marked as experimental.
+* For compatibility with Safari, every audio file used for games MUST be more than 1.5 seconds long. Add silence to reach the 1.5secs length.
+* Safari is having troubles on downloading audio for unknown reasons. Seems fixed on nightly builds. For now AUDIO IS EXPERIMENTAL.
\ No newline at end of file
diff --git a/deps/akihabara-core-1.3.1/README.markdown b/deps/akihabara-core-1.3.1/README.markdown
new file mode 100644
index 0000000..757ebda
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/README.markdown
@@ -0,0 +1,28 @@
+Akihabara
+=========
+
+Akihabara is a set of libraries, tools and presets to create pixelated indie-style 8/16-bit era games in Javascript that runs in your browser without any Flash plugin, making use of a small small small subset of the HTML5 features, that are actually available on many modern browsers.
+
+Notes for developers
+--------------------
+
+* For maximum compatibility make sure that you're using the ["name"] for when setting object properties with reserved names like "goto" and "data" (Discovered during patching for Wii)
+* Also do not use the comma after the last element of an array or a property of an object. Still work on many browsers but is broken for Opera Wii. (and probably IE, when will be supported)
+* For making sure that your sub-scripts are loaded, try to add an "alert" at the end. Opera for Wii silently fail when there are syntax errors like the one explained before.
+* Opera Wii wants that canvas have to be blitted at least once before being used - or fails with a browser crash! The built-in gbox.createCanvas was already fixed. Is a good thing to use that method for spawning canvas.
+
+Todo
+----
+
+* Akihabara needs to be documented! (Darren and Darius are wrapping up tutorials and docs! - some scripts for generating docs form sources are needed)
+* Better embeddability keeping playability on mobile
+* Solve randomly blinking sprites on Wii (?)
+
+Improvement
+-----------
+
+* Audio compatibility *Work in progress*
+
+Nice to have
+----
+* Networking
\ No newline at end of file
diff --git a/deps/akihabara-core-1.3.1/akihabara/buttons.png b/deps/akihabara-core-1.3.1/akihabara/buttons.png
new file mode 100644
index 0000000..3b056bb
Binary files /dev/null and b/deps/akihabara-core-1.3.1/akihabara/buttons.png differ
diff --git a/deps/akihabara-core-1.3.1/akihabara/debugfont.png b/deps/akihabara-core-1.3.1/akihabara/debugfont.png
new file mode 100644
index 0000000..3564b40
Binary files /dev/null and b/deps/akihabara-core-1.3.1/akihabara/debugfont.png differ
diff --git a/deps/akihabara-core-1.3.1/akihabara/docs/code.css b/deps/akihabara-core-1.3.1/akihabara/docs/code.css
new file mode 100644
index 0000000..57271dc
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/akihabara/docs/code.css
@@ -0,0 +1,16 @@
+body {
+ margin: 0;
+}
+.KEYW { color: #6277AA; font-weight: bold; }
+.COMM { color: #008200; }
+.NUMB { color: #393;}
+.STRN { color: #00d; }
+.REGX { color: #339;}
+.linenumber {
+ border-right: 7px solid #8DA6C6;
+ color: #fff;
+ font-style: normal;
+ font-weight: bold;
+ padding: 0 7px;
+ background: #6277AA;
+}
\ No newline at end of file
diff --git a/deps/akihabara-core-1.3.1/akihabara/docs/css/default.css b/deps/akihabara-core-1.3.1/akihabara/docs/css/default.css
new file mode 100644
index 0000000..b9dde3b
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/akihabara/docs/css/default.css
@@ -0,0 +1,418 @@
+/*
+ * TABLE OF CONTENTS:
+ * - Browser reset
+ * - HTML elements
+ * - JsDoc styling
+ */
+
+
+
+
+
+
+/*
+ * BEGIN BROWSER RESET
+ */
+
+body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,p,pre,form,fieldset,input,textarea,p,blockquote,th,td {
+ margin:0;
+ padding:0
+}
+html {
+ height:100%;
+ overflow:-moz-scrollbars-vertical;
+ overflow-x:auto
+}
+table {
+ border:0;
+ border-collapse:collapse;
+ border-spacing:0
+}
+fieldset,img {
+ border:0
+}
+address,caption,cite,code,dfn,em,strong,th,var {
+ font-style:normal;
+ font-weight:normal
+}
+em,cite {
+ font-style:italic
+}
+strong {
+ font-weight:bold
+}
+ol,ul {
+ list-style:none
+}
+caption,th {
+ text-align:left
+}
+h1,h2,h3,h4,h5,h6 {
+ font-size:100%;
+ font-weight:normal;
+ margin:0;
+ padding:0
+}
+q:before,q:after {
+ content:''
+}
+abbr,acronym {
+ border:0
+}
+
+/*
+ * END BROWSER RESET
+ */
+
+
+
+
+
+
+/*
+ * HTML ELEMENTS
+ */
+
+* {
+ line-height: 1.4em;
+}
+
+html {
+ font-size: 100%;
+}
+
+body {
+ font-size: 0.75em !important;
+ padding: 15px 0;
+ background: #eee;
+ background-image: -moz-linear-gradient(left, #dddddd, #f9f9f9);
+ background-image: -webkit-gradient(linear,left bottom,right bottom,color-stop(0, #dddddd),color-stop(1, #f9f9f9));
+ }
+
+body,
+input,
+select,
+textarea {
+ color: #000;
+ font-family: Arial, Geneva, sans-serif;
+}
+
+a:link,
+a:hover,
+a:active,
+a:visited {
+ color: #19199e;
+}
+a:hover,
+a:focus {
+ color: #00f;
+ text-decoration: none;
+}
+
+p {
+ margin: 0 0 1.5em 0;
+}
+
+/*
+ * END HTML ELEMENTS
+ */
+
+
+
+/*
+ * BEGIN HACK
+ */
+
+div.containerMain:after,
+div.safeBox:after {
+ content:"";
+ display:block;
+ height:0;
+ clear:both;
+}
+
+/*
+ * END HACK
+ */
+
+
+
+/*
+ * BEGIN JSDOC
+ */
+
+div.index *.heading1 {
+ margin-bottom: 0.5em;
+ border-bottom: 1px solid #999999;
+ padding: 0.5em 0 0.1em 0;
+ font-family: 'Lucida Sans Unicode', 'Lucida Grande', sans-serif;
+ font-size: 1.3em;
+ letter-spacing: 1px;
+}
+
+div.index {
+ float: left;
+ width: 30%;
+ min-width: 100px;
+ max-width: 250px;
+}
+div.index div.menu {
+ margin: 0 15px 0 -15px;
+ -moz-border-radius: 15px;
+ -webkit-border-radius: 15px;
+ border-radius: 15px;
+ padding: 15px 15px 15px 30px;
+ background-color: #FFFFFF;
+ background-color: rgba(255, 255, 255, 0.5);
+ -moz-box-shadow: 0px 0px 10px #c4c4c4;
+ -webkit-box-shadow: 0px 0px 10px #c4c4c4;
+ box-shadow: 0px 0px 10px #c4c4c4;
+}
+*+html div.index div.menu {
+ background-color: #FFFFFF;
+}
+* html div.index div.menu {
+ background-color: #FFFFFF;
+}
+
+div.index div.menu div {
+ text-align: left;
+}
+
+div.index div.menu a {
+ text-decoration: none;
+}
+div.index div.menu a:hover {
+ text-decoration: underline;
+}
+
+div.index ul.classList a {
+ font-family: Consolas, "Courier New", Courier, monospace;
+}
+
+div.index div.fineprint {
+ padding: 15px 30px 15px 15px;
+ color: #777;
+ font-size: 0.9em;
+}
+div.index div.fineprint a {
+ color: #777;
+}
+
+
+
+div.content {
+ float: left;
+ width: 70%;
+ min-width: 300px;
+ max-width: 600px;
+}
+div.innerContent {
+ padding: 0 0 0 2.5em;
+}
+
+div.content ul,
+div.content ol {
+ margin-bottom: 3em;
+}
+
+div.content *.classTitle {
+ margin-bottom: 0.5em;
+ font-family: 'Lucida Sans Unicode', 'Lucida Grande', sans-serif;
+ font-size: 2.5em;
+ letter-spacing: 2px;
+}
+
+div.content *.classTitle span {
+ font-family: Consolas, "Courier New", Courier, monospace;
+}
+
+div.content p.summary {
+ font-size: 1.2em;
+}
+
+div.content ul *.classname a,
+div.content ul *.filename a {
+ font-family: Consolas, "Courier New", Courier, monospace;
+ text-decoration: none;
+ font-weight: bold;
+}
+div.content ul *.classname a:hover,
+div.content ul *.filename a:hover {
+ text-decoration: underline;
+}
+
+div.content div.props {
+ position: relative;
+ left: -10px;
+ margin-bottom: 2.5em;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ padding: 10px 15px 15px 15px;
+ overflow: hidden;
+ background: #fff;
+ background: -moz-linear-gradient(top, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0.2)); /* FF3.6 */
+ background: -webkit-gradient(linear,left top,left bottom,color-stop(0, rgba(255, 255, 255, 0.7)),color-stop(1, rgba(255, 255, 255, 0.2)));
+ -moz-box-shadow: 0px 0px 10px #ccc;
+ -webkit-box-shadow: 0px 0px 5px #bbb;
+ box-shadow: 0px 0px 5px #bbb;
+}
+
+div.content div.props div.sectionTitle {
+ padding-bottom: 10px;
+ font-family: 'Lucida Sans Unicode', 'Lucida Grande', sans-serif;
+ font-size: 1.4em;
+ letter-spacing: 1px;
+}
+
+div.content div.hr {
+ margin: 0 10px 0 0;
+ height: 4em;
+}
+
+
+
+table.summaryTable {
+ position: relative;
+ left: -10px;
+ width: 100%;
+ border-collapse: collapse;
+ box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ -webkit-box-sizing: content-box;
+ -ms-box-sizing: content-box;
+ -o-box-sizing: content-box;
+ -icab-box-sizing: content-box;
+ -khtml-box-sizing: content-box;
+}
+
+table.summaryTable caption {
+ padding: 0 10px 10px 10px;
+ font-family: 'Lucida Sans Unicode', 'Lucida Grande', sans-serif;
+ font-size: 1.4em;
+ letter-spacing: 1px;
+}
+
+table.summaryTable td,
+table.summaryTable th {
+ padding: 0px 10px 10px 10px;
+ vertical-align: top;
+}
+table.summaryTable tr:last-child td {
+ padding-bottom: 0;
+}
+
+table.summaryTable th {
+ font-weight: bold;
+}
+
+table.summaryTable td.attributes {
+ width: 35%;
+ font-family: Consolas, "Courier New", Courier, monospace;
+ color: #666;
+}
+
+table.summaryTable td.nameDescription {
+ width: 65%
+}
+
+table.summaryTable td.nameDescription div.fixedFont {
+ font-weight: bold;
+}
+
+table.summaryTable div.description {
+ color: #333;
+}
+
+
+
+dl.detailList {
+ margin-top: 0.5em;
+}
+
+dl.detailList.nomargin + dl.detailList.nomargin {
+ margin-top: 0;
+}
+
+dl.detailList dt {
+ display: inline;
+ margin-right: 5px;
+ font-weight: bold;
+}
+
+dl.detailList dt:before {
+ display: block;
+ content: "";
+}
+
+dl.detailList dd {
+ display: inline;
+}
+
+dl.detailList.params dt {
+ display: block;
+}
+dl.detailList.params dd {
+ display: block;
+ padding-left: 2em;
+ padding-bottom: 0.4em;
+}
+
+
+
+
+ul.fileList li {
+ margin-bottom: 1.5em;
+}
+
+
+
+.fixedFont {
+ font-family: Consolas, "Courier New", Courier, monospace;
+}
+
+.fixedFont.heading {
+ margin-bottom: 0.5em;
+ font-size: 1.25em;
+ line-height: 1.1em
+}
+
+.fixedFont.heading + .description {
+ font-size: 1.2em;
+}
+
+.fixedFont.heading .light,
+.fixedFont.heading .lighter {
+ font-weight: bold;
+}
+
+pre.code {
+ margin: 10px 0 10px 0;
+ padding: 10px;
+ border: 1px solid #ccc;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ border-radius: 2px;
+ overflow: auto;
+ font-family: Consolas, "Courier New", Courier, monospace;
+ background: #eee;
+}
+
+.light {
+ color: #666;
+}
+
+.lighter {
+ color: #999;
+}
+
+.clear {
+ clear: both;
+ width: 100%;
+ min-height: 0;
+}
+
+/*
+ * END JSDOC
+ */
\ No newline at end of file
diff --git a/deps/akihabara-core-1.3.1/akihabara/docs/default.css b/deps/akihabara-core-1.3.1/akihabara/docs/default.css
new file mode 100644
index 0000000..84c1a4d
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/akihabara/docs/default.css
@@ -0,0 +1,440 @@
+/* --- Basic Layout -------------------------------------------------------- */
+/* For IE for Windows \*/
+* html{
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+}
+* html body{
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+}
+
+* html .fixed{
+ position: absolute;
+}
+.fixed {
+ position: fixed;
+ overflow: auto;
+}
+/* */
+/* --- Basic Layout -------------------------------------------------------- */
+body {
+ padding: 0;
+ margin: 0;
+ background: #fafafa;
+ font: 100% Verdana, Arial, Helvetica, sans-serif;
+}
+
+#main {
+ border-left: 0.625em #356aa0 solid;
+ margin-left: 16.25em;
+}
+
+#index {
+ top: 0;
+ left: 0;
+ width: 16.25em;
+ height: 100%;
+ overflow: auto;
+ overflow-x: hidden;
+ border-right: 0.625em solid #356aa0;
+}
+/* --- General layout ------------------------------------------------------ */
+h1 {
+ margin: 0.625em 0 0.625em;
+ font-size: 1.5em;
+ font-weight: bold;
+}
+
+h1.classTitle {
+ color: #007b00;
+ font-size: 1.5em;
+ line-height:130%;
+}
+
+h2 {
+ font-size: 1.25em;
+ color: #6277AA;
+ margin: 2em 0pt 1em;
+}
+
+h3 {
+ font-size: 1.1em;
+ color: #6277AA;
+}
+
+p {
+ margin: 1em 0;
+ text-align: justify;
+}
+
+a:link {
+ color: #001C58;
+}
+
+a:visited {
+ color: #6277AA;
+}
+
+hr {
+ border: none 0;
+ border-top: 0.0625em solid #7f8fb1;
+ height: 0.0625em;
+}
+
+.invisible {
+ display: none !important;
+}
+
+.clear {
+ clear: both;
+ line-height: 0.0625em;
+}
+
+.description {
+ font: 0.8125em Verdana, Arial;
+ line-height: 0.9375em;
+ padding: 0.25em;
+ padding-left: 0.25em;
+}
+
+.fineprint {
+ padding-top: 1.25em;
+ text-align: right;
+ font-size: 0.625em;
+}
+
+div.sectionTitle {
+ background-color: #356aa0;
+ color: #fff;
+ font-size: 1.1em;
+ font-weight: bold;
+ text-align: left;
+ padding: 0.125em 0.5em 0.125em 0.5em;
+ border: 0.0625em #7F8FB1 solid;
+}
+
+/* --- Index toggler ------------------------------------------------------- */
+#index-toggler {
+ width: 1em;
+ top: 0.25em;
+ left: 16.25em;
+ cursor: pointer;
+ font-weight: bold;
+ font-family: Arial;
+ font-size: 1em;
+ color: #fff;
+ z-index: 100;
+}
+#index-close {
+ background: url(images/never_translate.png) no-repeat left;
+ height: 1em;
+ width: 1em;
+ float: left;
+ cursor: pointer;
+ margin: 0.0625em 0.25em;
+ display: inline;
+}
+body.index-collapsed { background-position: 0; }
+body.index-collapsed #main { margin-left: 0; }
+body.index-collapsed #index { width: 0; }
+body.index-collapsed #index-wrapper { display: none; }
+body.index-collapsed #index-toggler { left: 0; }
+
+/* --- Module Filter ------------------------------------------------------- */
+.module-filter {
+ display: inline;
+ float: left;
+ cursor: pointer;
+ width: 4em;
+ font-size: 0.8em;
+ text-align: center;
+ padding-top: 0.125em;
+}
+.module-filter, .module-filter a {
+ text-decoration: none;
+ color: #007B00 !important;
+}
+body.filter .sectionItem { display: none; }
+body.filter .module-filter, body.filter .module-filter a { color: red !important; }
+body.filter.module-core .module-core { display: block; }
+body.filter.module-dom .module-dom { display: block; }
+body.filter.module-css .module-css { display: block; }
+body.filter.module-ajax .module-ajax { display: block; }
+body.filter.module-event .module-event { display: block; }
+
+/* --- Method Toggler ------------------------------------------------------ */
+.method-toggler {
+ background: url(images/minus.gif) no-repeat left;
+ cursor: pointer;
+ float: left;
+ width: 0.875em;
+ height: 0.875em;
+}
+.method-toggler.toggled {
+ background: url(images/plus.gif) no-repeat left;
+}
+
+/* --- Class Name and File Name Icons -------------------------------------- */
+.itemName .icon, .itemName .icon a {
+ color: #007B00 !important;
+}
+.itemName .icon {
+ font-size: 1em;
+ font-weight: bold;
+ padding: 0 0 0.125em 1.5em;
+}
+
+.itemName .icon {
+ background: url(images/class_obj.png) no-repeat left;
+}
+
+.itemName.namespace .icon {
+ background-image: url(images/namespace_obj.png) !important;
+}
+
+.itemName.namespace-function .icon {
+ background-image: url(images/namespace_function_obj.png) !important;
+}
+
+.fileName .icon {
+ background: url(images/file_obj.png) no-repeat left !important;
+}
+
+/* --- Class Method and Property Icons ------------------------------------- */
+div.classMethod .icon {
+ padding-left: 1.275em;
+ background: url(images/method_public_obj.png) no-repeat left;
+}
+
+div.classMethod .private {
+ background-image: url(images/method_private_obj.png) !important;
+}
+
+div.classProperty .icon {
+ padding-left: 1.275em;
+ background: url(images/field_public_obj.png) no-repeat left;
+}
+
+div.classProperty .private {
+ background-image: url(images/field_private_obj.png) !important;
+}
+
+
+/* --- Headers ------------------------------------------------------------- */
+.header {
+ color: #fff;
+ font-weight: bold;
+ font-size: 1.4em;
+ height: 1.2em;
+ background: #356aa0;
+ padding: 0.15em 0.4375em;
+}
+
+#index .header {
+ padding: 0.5em 0 0 0;
+ height: 1.625em;
+ font-size: 1em;
+ background: #356aa0;
+}
+
+/* --- Main/Index Shared Layouts ------------------------------------------- */
+#content .section {
+ margin-top: 1.5em;
+}
+
+div.classList {
+ padding: 0.625em;
+ font-family: arial, sans-serif;
+ font-size: 0.75em;
+}
+
+div.sectionItem .signature {
+ color: #000;
+ margin-left: 0.250em;
+ font-weight: normal;
+}
+
+.help, div.sectionItem .signature span {
+ cursor: help;
+}
+
+/* --- Main layout --------------------------------------------------------- */
+#main .full-description {
+ font-size: 0.9em;
+ line-height: 135%;
+}
+
+#main code {
+ font-size: 1.1em;
+ font-weight: bold;
+ color: #666;
+}
+
+html:not([lang*=""]) #main code {
+ font-size: 1.4em !important; /* Firefox hack */
+}
+
+#main p p, #main .full-description p, #main span a {
+ font-size: 1em;
+}
+
+#content {
+ padding: 0 1.25em 1.25em 1.25em;
+}
+
+#main .sectionItem {
+ background: #fff;
+ padding-left: 0.8em;
+}
+
+#main .sectionItem .description {
+ font-size: 0.8em;
+ padding-left: 0.4375em;
+ color: #777;
+}
+
+#main .summary .sectionItems, #main .details .sectionItem {
+ border: 0.25em #8DA6C6 solid;
+ padding: 0.625em;
+}
+
+/* --- Summary layout ------------------------------------------------------ */
+#main .summary {
+ background: #fff;
+}
+
+#main .summary .sectionItems {
+ border-width: 0.125em 0.125em 0.250em;
+}
+
+#main .summary .sectionItem {
+ padding: 0 0 0.3em 1.6em;
+ font-family: Verdana, Arial;
+ font-size: 0.875em;
+}
+
+#main .summary .sectionItem b {
+ margin-left: -1.4286em;
+}
+
+#main .summary .module-filter {
+ margin-left: -5.3571em;
+}
+
+/* --- Details layout ------------------------------------------------------ */
+#main .details .module-filter {
+ font-size: 70%;
+}
+
+#main .details .sectionItem {
+ border-width: 0.125em 0.125em 0.250em;
+ margin-bottom: 0.75em;
+}
+
+#main .details .itemTitle {
+ color: #001C58;
+ font-family: Verdana;
+ font-weight: bold;
+ font-size: 1em;
+ display: inline;
+}
+
+#main .details .itemTitle .light {
+ font-size: 0.9em;
+}
+
+#main .details .full-description {
+ padding: 0 0.625em;
+}
+
+
+.light, .light a:link, .light a:visited {
+ color: #777;
+ font-weight: normal;
+ font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
+}
+
+/* --- Details data -------------------------------------------------------- */
+table.methodItem {
+ font-size: 0.8em;
+}
+table.methodItem p {
+ margin: 0;
+}
+table.methodItem td {
+ padding: 0 0.1em 0.1em;
+ vertical-align: text-top;
+}
+table.methodItem .methodItemType {
+ text-align: center;
+}
+div.heading {
+ color: #6277AA;
+ font-size: 0.875em;
+ font-weight: bold;
+ margin: 0.625em 0 0;
+}
+
+/* --- Index Layout -------------------------------------------------------- */
+#index-wrapper {
+ width: 100%;
+}
+
+#index-menu {
+ background: #8DA6C6;
+ color: #fff;
+ text-align: center;
+ padding: 0.125em 0 0.25em;
+ font-weight: bold;
+ font-size: 0.9em;
+}
+
+#index-menu a:link, #index-menu a:visited {
+ color: #fff;
+}
+
+#index-title {
+ float: left;
+}
+
+#index-content {
+ background: #fff;
+ border: 0 #8DA6C6 solid;
+ border-width: 0 0 0.25em;
+ font-size: 1.1em;
+}
+
+#index-content .signature {
+ font-size: 0.85em;
+}
+
+#index .section {
+ padding-bottom: 0.625em;
+}
+
+/* --- Class index / File index layout ------------------------------------- */
+* html #content.index-page {
+ width: 92%; /* IE only hack para evitar bug no layout */
+}
+
+#content.index-page .sectionItem {
+ padding: 0.125em;
+}
+
+#content.index-page .sectionItems {
+ border-top-width: 0.125em;
+}
+
+#content.index-page .summary {
+ margin: 0;
+}
+
+/* --- Hide private members ------------------------------------------------ */
+/*
+div.sectionItem.private {
+ display: none !important;
+}
+/**/
diff --git a/deps/akihabara-core-1.3.1/akihabara/docs/files.html b/deps/akihabara-core-1.3.1/akihabara/docs/files.html
new file mode 100644
index 0000000..6085b5a
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/akihabara/docs/files.html
@@ -0,0 +1,180 @@
+
+
+
+ Gamebox module allows multiple grouped objects to move simultaneously, it helps with collisions,
rendering and moving objects. It also provides monospaced pixel-font rendering, keyboard handling,
audio, double buffering and eventually FSEs. Gamebox can also store and load data from cookies!
+
+ Help module provides some Javascript-specific functions, such object copying, randomizing functions,
string/array handlers and the akihabaraInit function.
+
+ Tool module provides simple developing tools. Currently, this file only has a cel-composer:
+it can compose an image stacking a set of frames for animating objects, applying a
+number of filters to each frame.
+
+ Toys module provides lots of common routines during the game developing:
+from effects for screen titles to HUD handling to platform/SHMUP/RPG oriented routines,
+like jumping characters, Z-Indexed objects, bullets, sparks, staff rolls, bonus screens, dialogues etc.
+
Gamebox module allows multiple grouped objects to move simultaneously, it helps with collisions,
rendering and moving objects. It also provides monospaced pixel-font rendering, keyboard handling,
audio, double buffering and eventually FSEs. Gamebox can also store and load data from cookies!
+
+ addImage(id, filename)
+ Adds an image file to the loader, assigning it to an ID. If adding an image to an existing ID, it checks to see if the file you're adding is different than the one currently assigned to the ID.
+
+
+
+
+ addObject(data)
+ Creates a new game object. Generally speaking you pass a fully-defined object as the parameter (including a group, id, tileset, and so on).
+
+
+ clearGroup(gid)
+ Destroys all objects in a given group.
+
+
+
+
+ collides(o1, o2, t)
+ Calculates a box collision between two collision boxes within a given tolerance. A higher tolerance means less precise collision.
+
+
+
+
+ createCanvas(id, data)
+ Creates a new canvas. By default, the width and height is the current gbox.
+
+
+
+
+ dataClear(k)
+ Clears a value stored in a key-value pair in a browser cookie. Sets value to "".
+
+
+
+
+ dataLoad(k, a)
+ Loads data from a browser cookie. Send it a key and it returns a value (if available).
+
+
+
+
+ dataSave(k, v, d)
+ Saves data to a browser cookie as a key-value pair, which can be restored later using gbox.dataLoad.
+
+
+
+
+ debugGetstats()
+ Displays basic audio, object, and performance statistics in the status bar. Automatically called each frame if the status bar is enabled.
+
+
+ getCanvasContext(id)
+ Gets the two-dimensional canvas context of a given canvas. The object it returns contains all the drawing functions for the canvas.
+
+
+ getFlag(f)
+ Returns a gbox flag at index f.
+
+
+
+
+ getFont(id)
+ Returns a font object containing data about the font.
+
+
+
+
+ getFps()
+ Get the frames per second rate (default is 25).
+
+
+
+
+ getGroups()
+ Get an array containing the names of each group in the game, in order of rendering.
+
+
+
+
+ getImage(id)
+ Gets information about a loaded image.
+
+
+
+
+ getObject(group, id)
+ Given a group and an id for a particular object instance, this returns the instance requested. NOTE: this does not return a copy of the object you've requested! Any modifications you make to the object returned are directly modifying the object you requested.
+
+
+ imageIsLoaded(id)
+ Checks to see if an image was successfully loaded.
+
+
+
+
+ initScreen(w, h)
+ Initializes the screen to a certain width and height, applies zoom attributes, populates the body of the HTML document including the canvas element, sets an initial camera, creates a '_buffer' canvas, sets keyboard event listeners, and many other initialization functions.
+
+
+
+
+ keyIsHit(id)
+ Returns true if a given key in this._keymap is pressed.
+
+
+
+
+ keyIsHold(id)
+ Returns true if a given key in this._keymap has been held down for at least one frame.
+
+
+
+
+ keyIsPressed(id)
+ Returns true if a given key in this._keymap is being held down.
+
+
+
+
+ keyIsReleased(id)
+ Returns true if a given key in this._keymap is released.
+
+
+
+
+ loadAll(cb)
+ Loads the initial splash screen and debugging font, then calls gbox._waitforloaded which adds to the game all the previously defined resources.
+
+
+ objectIsTrash(o)
+ Returns whether an object is due to be trashed. Useful in cases you want to check if an object is marked as trash before it is actually deleted.
+
+
+
+
+ objectIsVisible(obj)
+ Determines whether an object is visible by seeing if it collides with the camera's viewport.
+
+
+
+
+ pixelcollides(o1, o2, t)
+ Calculates a point-box collision between a point and a collision box within a given tolerance. A higher tolerance means less precise collision.
+
+
+
+
+ playAllGroups()
+ Enables all groups, toggling any groups that are currently disabled.
+
image: reference to the font image loaded (must contain font character tiles in ASCII order)
firstletter: the ASCII character that the font image's first character corresponds to
tileh: height in pixels of the character tiles
tilew: width in pixels of the character tiles
tilerow: width in pixels of each row in the font image
gapx: x-coord gap between tile columns, in pixels
gapy: y-coord gap between tile rows, in pixels
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.addImage(id, filename)
+
+
+
+ Adds an image file to the loader, assigning it to an ID. If adding an image to an existing ID, it checks to see if the file you're
adding is different than the one currently assigned to the ID. If it's different, it overwrites the old image. If it's the same, then
no action is taken.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ id
+
+
+ The identifier of the image.
+
+
+
+
+
+ {String}
+
+
+ filename
+
+
+ The file name of the image.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.addObject(data)
+ : Object
+
+
+ Creates a new game object. Generally speaking you pass a fully-defined object as the parameter (including a group, id, tileset, and so on).
A group must be specified, or the program will crash. If no id is specified, then it will automatically provide
an id of 'obj-XXXX' where 'XXXX' is an automatically incrementing integer. This is where the initialize, first, and blit
functions are defined, as well.
+
+
+
+
+
+ Creates a new Akihabara tileset, adding it to the engine.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ t
+
+
+ An object containing:
id {String}: the new id of the tileset
image {String}: reference to the tileset image loaded
tileh {Integer}: height in pixels of the tiles
tilew {Integer}: width in pixels of the tiles
tilerow {Integer}: width in pixels of each row in the font image
gapx {Integer}: x-coord gap between tile columns, in pixels
gapy {Integer}: y-coord gap between tile rows, in pixels
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.blit(tox, image, data)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ tox
+
+
+
+
+
+
+
+
+
+
+
+ image
+
+
+
+
+
+
+
+
+
+
+
+ data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.blitAll(tox, image, data)
+
+
+
+ Draws an image to a canvas context
+
+
+
+
+
// draw an image at (100,100)
gbox.blitAll(gbox.getBufferContext(),gbox.getImage("image_id"),{dx:100,dy:100});
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ tox
+
+
+ The canvas context to be drawn on.
+
+
+
+
+
+ {Object}
+
+
+ image
+
+
+ The image to draw. Must be a DOM Image element, typicallly accessed via gbox.getImage
+
+
+
+
+
+ {Object}
+
+
+ data
+
+
+ An object containing data about the tile to be drawn, including:
dx {Integer}: (required) x coordinate to draw the image at
dy {Integer}: (required) y coordinate to draw the image at
fliph {Integer}: horizontal flip, either 1 or -1
flipv {Integer}: vertical flip, either 1 or -1
alpha {Float}: alpha value (0 is transparent, 1 is opaque)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.blitClear(image, data)
+
+
+
+ Clears a rectangular area of a canvas context.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ image
+
+
+ The canvas context to be drawn on.
+
+
+
+
+
+ {Object}
+
+
+ data
+
+
+ An object containing a set of data, including:
x {Integer}: (required) the x coordinate of the top-left corner of the rectangle
y {Integer}: (required) the y coordinate of the top-left corner of the rectangle
w {Integer}: the width of the box; defaults to canvas width
h {Integer}: the height the box; defaults to canvas height
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.blitFade(tox, data)
+
+
+
+ Draws a filled rectangle over an entire canvas context.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ tox
+
+
+ The canvas context to be filled.
+
+
+
+
+
+ {Object}
+
+
+ data
+
+
+ An object containing a set of data, including:
alpha {Float}: the alpha value of the rectangle; defaults to 1
color {Object}: the color of the box, formatted rgb(rValue, gValue, bValue); default black
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.blitImageToScreen(image)
+
+
+
+ Draws an image directly to the screen's current canvas context. Used internally in gbox.go(). Probably shouldn't be used otherwise.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ image
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.blitRect(tox, data)
+
+
+
+ Draws a filled rectangle to a canvas context.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ tox
+
+
+ The canvas context to be drawn on.
+
+
+
+
+
+ {Object}
+
+
+ data
+
+
+ An object containing a set of data, including:
x {Integer}: (required) the x coordinate of the top-left corner of the rectangle
y {Integer}: (required) the y coordinate of the top-left corner of the rectangle
w {Integer}: (required) the width of the box
h {Integer}: (required) the height the box
alpha {Float}: the alpha value of the rectangle; defaults to 1
color {Object}: the color of the box, formatted rgb(rValue, gValue, bValue); default black
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.blitText(tox, data)
+
+
+
+ Draws text to a canvas context
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ tox
+
+
+ The canvas context to be drawn on.
+
+
+
+
+
+ {Object}
+
+
+ data
+
+
+ An object containing a set of data, including:
font {String}: (required) the id of font to draw the text with
text {String}: (required) the text to display
dx {Integer}: (required) the x coordinate to draw the text at
dy {Integer}: (required) the y coordinate to draw the text at
dw {Integer}: the width of the text area -- required if you define data.halign
dh {Integer}: the height of the text area -- required if you define data.valign
valign {Integer}: either gbox.ALIGN_BOTTOM (aligns from the bottom of the text area) or gbox.ALIGN_MIDDLE (vertically centers text in text area)
halign {Integer}: either gbox.ALIGN_RIGHT (aligns to the right hand side of text area) or gbox.ALIGN_CENTER (horizontallly centers text in text area)
alpha {Float}: alpha value (0 is transparent, 1 is opaque)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.blitTile(tox, data)
+
+
+
+ Draws a tile to a canvas context
+
+
+
+
+
// from capman, draws an current object's tile, called from inside its blit function
gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:this.frame,dx:this.x,dy:this.y,fliph:this.fliph,flipv:this.flipv,camera:this.camera,alpha:1});
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ tox
+
+
+ The canvas context to be drawn on.
+
+
+
+
+
+ {Object}
+
+
+ data
+
+
+ An object containing data about the tile to be drawn, including:
tileset {String}: the id of the tileset
tile {Integer}: the index of the tile within the tileset to be drawn
dx {Integer}: x coordinate to draw the tile at
dy {Integer}: y coordinate to draw the tile at
fliph {Integer}: horizontal flip, either 1 or -1
flipv {Integer}: vertical flip, either 1 or -1
alpha {Float}: alpha value (0 is transparent, 1 is opaque)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.blitTilemap(tox, data)
+
+
+
+ Draws a tilemap to a canvas context
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ tox
+
+
+ The canvas context to be drawn on.
+
+
+
+
+
+ {Object}
+
+
+ data
+
+
+ An object containing a set of tilemap data, including:
tileset {String}: (required) the id of the tileset the tilemap is based on
map {Array}: an array whose x and y coord represent the tilemap coordinates, containing integers that correspond to the index of a given tile (or null for no tile)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.canLog()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.centerCamera(data, viewdata)
+
+
+
+ Centers the camera.
+
+
+
+
+
// Center the camera on the player object
gbox.centerCamera(gbox.getObject('player', 'player_id'), {w: map.w, h: map.h});
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ data
+
+
+ An object containing x and y parameters -- typically the object you wish to center the camera on.
+
+
+
+
+
+ {Object}
+
+
+ viewdata
+
+
+ An object containing parameters h and w, which are a bounding box that the camera is
not supposed to leave. For example, to use your map as a bounding area for the camera, pass along {w: map.w, h: map.h}.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.changeAudioVolume(a, vol)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+ vol
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.changeChannelVolume(ch, a)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ ch
+
+
+
+
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.clearCache()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.clearGroup(gid)
+
+
+
+ Destroys all objects in a given group.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ gid
+
+
+ The id of the group.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.collides(o1, o2, t)
+
+
+
+ Calculates a box collision between two collision boxes within a given tolerance. A higher tolerance means less precise collision.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ o1
+
+
+ A collision box you're testing for collision. Must contain:
x {Integer}: (required) the x coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
y {Integer}: (required) the y coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
w {Integer}: (required) the width of the box
h {Integer}: (required) the height the box
+
+
+
+
+
+ {Object}
+
+
+ o2
+
+
+ A collision box you're testing for collision. Must contain:
x {Integer}: (required) the x coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
y {Integer}: (required) the y coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
w {Integer}: (required) the width of the box
h {Integer}: (required) the height the box
+
+
+
+
+
+ {Integer}
+
+
+ t
+
+
+ The tolerance for the collision, in pixels. A value of 0 means pixel-perfect box collision. A value of 2 would mean that the
boxes could overlap by up to 2 pixels without being considered a collision.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ True if the two collision boxes are colliding within the given tolerance.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.createCanvas(id, data)
+
+
+
+ Creates a new canvas. By default, the width and height is the current gbox._screenw and gbox._screenh,
but it can also be set by passing in a data object with the appropriate parameters.
+
+
+
+
+
gbox.createCanvas('newCanvas', {w: 640, h: 480});
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ id
+
+
+ The id of the new canvas.
+
+
+
+
+
+ {Object}
+
+
+ data
+
+
+ (Optional) The height and width of the new canvas, contained in data.h and data.w parameters.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.dataClear(k)
+
+
+
+ Clears a value stored in a key-value pair in a browser cookie. Sets value to "". Only works if user has cookies enabled.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ k
+
+
+ The key which identifies the value you are clearing.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.dataLoad(k, a)
+ : Object
+
+
+ Loads data from a browser cookie. Send it a key and it returns a value (if available). Only works if user has cookies enabled.
+
+
+
+
+
hiscore = gbox.dataLoad("hiscore");
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ k
+
+
+ The key which identifies the value you are loading.
+
+
+
+
+
+ {String}
+
+
+ a
+
+
+ A switch to determine whether a string or a number is returned. By default a string is returned.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Object}
+
+
+ A string or a number loaded from the cookie.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.dataSave(k, v, d)
+
+
+
+ Saves data to a browser cookie as a key-value pair, which can be restored later using gbox.dataLoad. Only
works if user has cookies enabled.
+
+
+
+
+
+ The key which identifies the value you are storing.
+
+
+
+
+
+ {String}
+
+
+ v
+
+
+ The value you wish to store. Needs to be a string!
+
+
+
+
+
+ {String}
+
+
+ d
+
+
+ A date offset, to be added to the current date. Defines the cookie's expiration date. By default this is set to 10 years.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.debugGetstats()
+
+
+
+ Displays basic audio, object, and performance statistics in the status bar. Automatically called each frame if the status bar is enabled.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.deleteAudio(id)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.deleteCanvas(id)
+
+
+
+ Deletes a given canvas.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ id
+
+
+ The id of the canvas to be deleted.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.deleteImage(id)
+
+
+
+ Deletes an image currently in use. Does not delete the image file, but removes it from Akihabara's image list.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ id
+
+
+ The identifier of the image.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getAudioDuration(a)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getAudioIsSingleChannel()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getAudioMute(a)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getAudioPosition(a)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getAudioVolume(a, vol)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+ vol
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getBuffer()
+ : Object
+
+
+ Gets the buffer canvas (automatically created by gbox.initScreen).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Object}
+
+
+ A DOM Canvas element, including the width and height of the canvas.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getBufferContext()
+ : Object
+
+
+ Gets the buffer canvas context.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Object}
+
+
+ A DOM Canvas context object.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getCamera()
+ : Object
+
+
+ Gets the current camera object.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Object}
+
+
+ The camera object.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getCanvas(id)
+ : Object
+
+
+ Gets a given canvas.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ id
+
+
+ The identifier of the canvas.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Object}
+
+
+ A DOM Canvas element, including the width and height of the canvas.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getCanvasContext(id)
+ : Object
+
+
+ Gets the two-dimensional canvas context of a given canvas. The object it returns contains all the drawing functions for the canvas.
See W3C and
Mozilla Developer Center for details.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ id
+
+
+ The identifier of the canvas.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Object}
+
+
+ A DOM Canvas context object.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getChannelDefaultVolume(ch)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ ch
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getChannelVolume(ch)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ ch
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getFlag(f)
+
+
+
+ Returns a gbox flag at index f.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ f
+
+
+ The index of the flag you want returned.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getFont(id)
+
+
+
+ Returns a font object containing data about the font.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ id
+
+
+ The id of the font, as set in gbox.addFont.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getFps()
+ : Integer
+
+
+ Get the frames per second rate (default is 25).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Integer}
+
+
+ Returns the frames per second.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getGroups()
+ : Array
+
+
+ Get an array containing the names of each group in the game, in order of rendering.
+
+
+
+
+
+ Gets information about a loaded image.
+
+
+
+
+
image = gbox.getImage('logo');
image; // => ?
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ id
+
+
+ The id of the image.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Object}
+
+
+ A DOM Image element, including the URL and last modified date of the image, its ID, and whether it was loaded successfully.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getObject(group, id)
+ : Object
+
+
+ Given a group and an id for a particular object instance, this returns the instance requested.
NOTE: this does not return a copy of the object you've requested! Any modifications you make
to the object returned are directly modifying the object you requested.
+
+
+
+
+
// Find the player and reduce health by half.
playertemp = gbox.getObject('player','player_id');
player.health = player.health/2;
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ group
+
+
+ The id of the group that contains the object.
+
+
+
+
+
+ {String}
+
+
+ id
+
+
+ The id of the instance of the object.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Object}
+
+
+ The object requested.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getScreenH()
+ : Integer
+
+
+ Get the screen height.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Integer}
+
+
+ Screen height in pixels.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getScreenHH()
+ : Integer
+
+
+ Get the screen half-height.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Integer}
+
+
+ Screen half-height in pixels.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getScreenHW()
+ : Integer
+
+
+ Get the screen half-width.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Integer}
+
+
+ Screen half-width in pixels.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getScreenW()
+ : Integer
+
+
+ Get the screen width.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Integer}
+
+
+ Screen width in pixels.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.getTiles(t)
+
+
+
+ Gets an Akihabara tileset, adding it to the engine.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ t
+
+
+ The ID of a tileset.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ An object containing:
id {String}: the new id of the tileset
image {String}: reference to the tileset image loaded
tileh {Integer}: height in pixels of the tiles
tilew {Integer}: width in pixels of the tiles
tilerow {Integer}: width in pixels of each row in the font image
gapx {Integer}: x-coord gap between tile columns, in pixels
gapy {Integer}: y-coord gap between tile rows, in pixels
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.go()
+
+
+
+ This function is called once per frame. This is where the basic rendering and processing of groups occurs.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.groupIsEmpty(gid)
+ : Boolean
+
+
+ Returns whether a given group contains no objets.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ gid
+
+
+ The group you're checking.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Boolean}
+
+
+ True if the group contains no objects.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.hitAudio(a, data)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+ data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.imageIsLoaded(id)
+ : Boolean
+
+
+ Checks to see if an image was successfully loaded.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ id
+
+
+ The id of the image.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Boolean}
+
+
+ True if the image has been loaded.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.initScreen(w, h)
+
+
+
+ Initializes the screen to a certain width and height, applies zoom attributes, populates the
body of the HTML document including the canvas element, sets an initial camera, creates a '_buffer'
canvas, sets keyboard event listeners, and many other initialization functions.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Integer}
+
+
+ w
+
+
+ The width of the main canvas.
+
+
+
+
+
+ {Integer}
+
+
+ h
+
+
+ The height of the main canvas.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.keyIsHit(id)
+ : Boolean
+
+
+ Returns true if a given key in this._keymap is pressed. Only returns true on the transition from unpressed to pressed.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ id
+
+
+ A key in the keymap. By default, one of: "up" "down" "left" "right" "a" "b" "c"
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Boolean}
+
+
+ True if the given key is transitioning from unpressed to pressed in this frame.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.keyIsHold(id)
+ : Boolean
+
+
+ Returns true if a given key in this._keymap has been held down for at least one frame. Will not return true if a key
is quickly tapped, only once it has been held down for a frame.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ id
+
+
+ A key in the keymap. By default, one of: "up" "down" "left" "right" "a" "b" "c"
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Boolean}
+
+
+ True if the given key has been held down for at least one frame.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.keyIsPressed(id)
+ : Boolean
+
+
+ Returns true if a given key in this._keymap is being held down. Returns true as long as the key is held down.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ id
+
+
+ A key in the keymap. By default, one of: "up" "down" "left" "right" "a" "b" "c"
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Boolean}
+
+
+ True if the given key is held down.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.keyIsReleased(id)
+ : Boolean
+
+
+ Returns true if a given key in this._keymap is released. Only returns true on the transition from pressed to unpressed.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ id
+
+
+ A key in the keymap. By default, one of: "up" "down" "left" "right" "a" "b" "c"
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Boolean}
+
+
+ True if the given key is transitioning from pressed to unpressed in this frame.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.loadAll(cb)
+
+
+
+ Loads the initial splash screen and debugging font, then calls gbox._waitforloaded which adds to the game all the previously
defined resources. Once gbox._waitforloaded is done, it calls the callback function cb.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ cb
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.log()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.objectIsTrash(o)
+ : Boolean
+
+
+ Returns whether an object is due to be trashed. Useful in cases you want to check if
an object is marked as trash before it is actually deleted.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ o
+
+
+ The object you're checking.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+ {Boolean}
+
+
+ True if the object is marked as trash.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.objectIsVisible(obj)
+
+
+
+ Determines whether an object is visible by seeing if it collides with the camera's viewport.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ obj
+
+
+ The object you're testing to see if it's visible. Must contain:
x {Integer}: (required) the x coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
y {Integer}: (required) the y coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
w {Integer}: (required) the width of the object's collision box
h {Integer}: (required) the height the object's box
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ True if the object's collision box is within the camera's viewport.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.pixelcollides(o1, o2, t)
+
+
+
+ Calculates a point-box collision between a point and a collision box within a given tolerance. A higher tolerance means less precise collision.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ o1
+
+
+ A point you're testing for collision. Must contain:
x {Integer}: (required) the x coordinate of the point
y {Integer}: (required) the y coordinate of the point
+
+
+
+
+
+ {Object}
+
+
+ o2
+
+
+ A collision box you're testing for collision. Must contain:
x {Integer}: (required) the x coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
y {Integer}: (required) the y coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
w {Integer}: (required) the width of the box
h {Integer}: (required) the height the box
+
+
+
+
+
+ {Integer}
+
+
+ t
+
+
+ The tolerance for the collision, in pixels. A value of 0 means pixel-perfect collision. A value of 2 would mean that the
point could exist within the outermost 2 pixels of the box without being considered a collision.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ True if the point is colliding with the box within the given tolerance.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.playAllGroups()
+
+
+
+ Enables all groups, toggling any groups that are currently disabled.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.playAudio(a, data)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+ data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.playGroup(gid)
+
+
+
+ If a group is disabled, this will enable the group.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ gid
+
+
+ The id of the group.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.playGroups(gid)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ gid
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.purgeGarbage(obj)
+
+
+
+ Clears the record held in gbox._garbage of what has been deleted.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ obj
+
+
+ The object you wish to delete.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.readBundleData(pack, call)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ pack
+
+
+
+
+
+
+
+
+
+
+
+ call
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.resetChannel(ch)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ ch
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setAudioChannels(a)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setAudioCompatMode(m)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ m
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setAudioCreateMode(m)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ m
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setAudioDequeueTime(m)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ m
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setAudioMute(a)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setAudioPosition(a, p)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+ p
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setAudioPositionDelay(m)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ m
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setAudioTeam(a)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setAudioUnmute(a)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setAudioVolume(a, vol)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+ vol
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setAutoskip(f)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ f
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setCallback(cb)
+
+
+
+ Deprecated: gbox._cb is now set by passing it directly into gbox.loadAll(). Left in for backwards compatibility.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ cb
+
+
+ The name of the function to be called once gbox.loadAll is completed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setCameraX(x, viewdata)
+
+
+
+ Sets the x value of the current camera object.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Integer}
+
+
+ x
+
+
+ The camera object's new x value.
+
+
+
+
+
+ {Object}
+
+
+ viewdata
+
+
+ An object containing parameters h and w, which are a bounding box that the camera is
not supposed to leave. For example, to use your map as a bounding area for the camera, pass along {w: map.w, h: map.h}.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setCameraY(y, viewdata)
+
+
+
+ Sets the y value of the current camera object.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Integer}
+
+
+ y
+
+
+ The camera object's new y value.
+
+
+
+
+
+ {Object}
+
+
+ viewdata
+
+
+ An object containing parameters h and w, which are a bounding box that the camera is
not supposed to leave. For example, to use your map as a bounding area for the camera, pass along {w: map.w, h: map.h}.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setCanAudio(a)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setCanLog(c)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ c
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setChannelVolume(ch, a)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ ch
+
+
+
+
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setDoubleBuffering(db)
+
+
+
+ Sets the gbox._db property. Turns on an off double buffering.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Boolean}
+
+
+ db
+
+
+ The value to write to gbox._db. True enables double buffering, false disables.
+
+ Total frames per second for the game to run at.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setFrameskip(f)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ f
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setGroups(g)
+
+
+
+ Defines the names of each group in the game along with their rendering order.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Array}
+
+
+ g
+
+
+ An array of strings of group names, in the order in which the groups should be rendered. So
g[0] will contain the first group to render, g[1] the second group to render, etc.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setLowerAudioTeam(a)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setRenderOrder(g)
+
+
+
+ A method of setting the render order of groups independently of gbox.setGroups. Sets gbox._renderorder,
which by default is equivalent to gbox._groups. However, gbox._renderorder is what ultimately determines
the rendering order of groups. If you need to change your rendering order on the fly, use this function
by passing it a reordered array of group names.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Array}
+
+
+ g
+
+
+ An array of strings of group names, in the order in which the groups should be rendered. So
g[0] will contain the first group to render, g[1] the second group to render, etc.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setScreenBorder(a)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setShowPlayers(m)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ m
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setSplashSettings(a)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setStatBar(txt)
+
+
+
+ Writes text to the status bar, but only if the status bar is enabled.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ txt
+
+
+ The text to write to the status bar.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setStatusBar(f)
+
+
+
+ Sets the gbox._statbar property. Only useful if called before gbox.initScreen. Debugging funtionality.
Much easier to access if you add '?statusbar=1' to your URL.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Boolean}
+
+
+ f
+
+
+ The value to write to gbox._statbar.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setZindex(th, z)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ th
+
+
+
+
+
+
+
+
+
+
+
+ z
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.setZoom(z)
+
+
+
+ Sets the gbox._zoom parameter, only works before gbox.initScreen is called.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Integer}
+
+
+ z
+
+
+ Zoom factor.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.soloGroup(gid)
+
+
+
+ Turns off all groups except for the one specified.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ gid
+
+
+ The id of the group.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.stopAudio(a, permissive)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+ permissive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.stopChannel(ch)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ ch
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.stopGroup(gid)
+
+
+
+ If a group is enabled, this will disable the group.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ gid
+
+
+ The id of the group.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.stopGroups(gid)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ gid
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.toggleGroup(gid)
+
+
+
+ Toggles a group between enabled and disabled status.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ gid
+
+
+ The id of the group.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.toggleGroups(gid)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ gid
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.trashGroup(group)
+
+
+
+ Deletes every object in a given group.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ group
+
+
+ The group id.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gbox.trashObject(obj)
+
+
+
+ Deletes an object, keeping a record of its group and id in gbox._garbage.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ obj
+
+
+ The object you wish to delete.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation generated by JsDoc Toolkit 2.3.2 on Wed Aug 18 2010 16:09:24 GMT-0400 (EDT)
+
Help module provides some Javascript-specific functions, such object copying, randomizing functions,
string/array handlers and the akihabaraInit function.
+
+ akihabaraInit(data)
+ This provides a number of configurations: fps, display zoom, dynamic frameskip, force touch parameters, etc. Many of these settings can be set manually by passing an object with the parameters defined, or via URL parameters.
+
+
+
+
+ asciiArtToMap(map, tra)
+ Takes an ascii-art-style array of characters and converts it to an Akihabara-compatible map format.
+
+
+
+
+ cloneObject(model)
+ Creates a duplicate of an existing set of parameters.
+
+
+
+
+ copyModel(data, model)
+ Merges two sets of parameters together overwriting any existing parameters. This merges model->data, and if data and model share parameters, data's are overwritten by model's.
+
+
+
+
+ createModel(obj, attrs)
+ Creates a subset of an existing set of parameters.
+
+
+
+
+ decideFrame(cnt, anim)
+ Determines which frame of a given animation to display. Will loop an animation.
+
+
+
+
+ decideFrameOnce(cnt, anim)
+ Determines which frame of a given animation to display. Will remain on the last frame when the animation has played once.
+
+
+
+
+ finalizeTilemap(map)
+ Calculates and sets the width and height (map.h, map.w) and half-width and half-height (map.hh, map.hw) of a map object.
+
+
+
+
+ framestotime(frames)
+ Converts a quantity of frames into a timestamp formatted "mm:ss:cs" (minutes, seconds, centiseconds). Calculated using the current frames per second.
+
+
+
+
+ getArrayCapped(a, id)
+ Returns the Nth element in an array. If the array is shorter than N, it returns the last element of the array.
+
+
+ getDeviceConfig()
+ Automatically configures a bunch of settings depending on the web browser and device that is viewing the game. Mostly sets the maximum number of audio channels and touch settings.
+
+
+
+
+ getTileInMap(x, y, map, ifout, mapid)
+ Given x,y coordinates and map information, this returns the tile at a given point.
+
+
+
+
+ geturlparameter(name)
+ Reads the value of a query parameter from the URL of the web page.
+
+
+
+
+ goToZero(v)
+ Subtracts or adds 1 to a value, always converging to zero. For example, passing -3 yields -2, 5 yields 4, etc.
+
+
+
+
+ isDefined(A)
+ Tests whether an object contains a given parameter.
+
+
+
+
+ isLastFrameOnce(cnt, anim)
+ Returns whether the animation was fully played at least once with decideFrame or fully with decideFrameOnce.
+
+
+
+
+ 0 means you're falling onto something else.">isSquished(th, by)
+ Tests to see if an object is being "jumped on" by another object. Only works for platformers, since it assumes accy>0 means you're falling onto something else.
+
+
+
+
+ limit(v, min, max)
+ Limits a number to a certain range. If the number is below the minimum, the minimum is returned.
+
+
+
+
+ mergeWithModel(data, model)
+ Merges two sets of parameters together without overwriting existing parameters. This merges from model to data, and if data and model share parameters, data's values remain intact.
+
+
+
+
+ multiplier(v, mul)
+ Multiplies two numbers together, returning the result, unless the first parameter is less than 2, in which case it returns 1.
+
+
+
+
+ objToStr(Any)
+ Writes the contents of an object to a string. Useful for debugging.
+
+
+
+
+ postpad(str, len, pad)
+ Postpends a string with repeated instances of another string until it the result is greater than or equal to a desired length.
+
+
+
+
+ prepad(str, len, pad)
+ Prepends a string with repeated instances of another string until it the result is greater than or equal to a desired length.
+
+
+
+
+ random(min, range)
+ Generates uniformly distributed random integers between min and min+range, non-inclusive. So help.random(0,2) will only return 0 and 1, etc.
+
+
+
+
+ seq(st, ed, skip)
+ Generates numbers from st to ed, along with a skip value.
+
+
+
+
+ setTileInMap(ctx, map, x, y, tile, The)
+ Sets a tile in the map and draws it. Does not return anything.
+
+
+
+
+ upAndDown(counter, max)
+ Given an incrementing value each step, this will return a value increasing from 0 until max/2, at which point it will decrement to 0, then go back up to max/2, in an endless cycle.
+
+
+
+
+ xPixelToTile(map, x, gap)
+ Converts an x-coordinate of a pixel to the x-coordinate of the tile column it's in. This effectively "snaps" an x coordinate to a tile edge.
+
+
+
+
+ xPixelToTileX(map, x, gap)
+ Converts an x-coordinate of a pixel to its corresponding tile x-coordinate.
+
+
+
+
+ yPixelToTile(map, y, gap)
+ Converts a y-coordinate of a pixel to the y-coordinate of the tile row it's in. This effectively "snaps" a y coordinate to a tile edge.
+
+
+
+
+ yPixelToTileY(map, y, gap)
+ Converts a y-coordinate of a pixel to its corresponding tile y-coordinate.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Method Detail
+
+
+
+
+
+
+
+ help.akihabaraInit(data)
+
+
+
+ This provides a number of configurations: fps, display zoom, dynamic frameskip, force touch parameters, etc. Many of these settings can
be set manually by passing an object with the parameters defined, or via URL parameters.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ data
+
+
+ An optional object containing parameters you wish to set. Works for data.zoom, data.splash, data.width, data.height, data.title, data.fps, and data.padmode.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.asciiArtToMap(map, tra)
+
+
+
+ Takes an ascii-art-style array of characters and converts it to an Akihabara-compatible map format.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Array}
+
+
+ map
+
+
+ An array of characters representing a map.
+
+
+
+
+
+ {Array}
+
+
+ tra
+
+
+ A translation array. This is an array of arrays, formatted like [ [null, char1], [0, char2], [1, char3] ]. There must at least be a null entry, followed by one numerical entry for each tile type you want to render, corresponding to the unique characters in the map array. The null entry maps a character to empty space.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ A map array formatted such that it can be attached to a map object.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.cloneObject(model)
+
+
+
+ Creates a duplicate of an existing set of parameters.
+
+
+
+
+
+ Merges two sets of parameters together overwriting any existing parameters. This merges model->data, and if data and model share parameters, data's are overwritten by model's.
+
+
+
+
+
+ An object containing a set of parameters, the destination of the merge.
+
+
+
+
+
+ {Object}
+
+
+ model
+
+
+ An object containing a set of parameters, the source of the merge.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ A merged model where the values of 'model' take precedence over those of 'data'. The 'data' object is returned and will be an exact copy of 'model', plus any parameters that 'data' had before the merge that 'model' did not.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.createModel(obj, attrs)
+
+
+
+ Creates a subset of an existing set of parameters.
+
+
+
+
+
+ An object containing a set of parameters, the source of the data.
+
+
+
+
+
+ {Array}
+
+
+ attrs
+
+
+ An array of strings, containing the names of parameters you wish to copy.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ A new set of parameters based on the subset specified.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.decideFrame(cnt, anim)
+
+
+
+ Determines which frame of a given animation to display. Will loop an animation.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Integer}
+
+
+ cnt
+
+
+ A global frame counter.
+
+
+
+
+
+ {Object}
+
+
+ anim
+
+
+ An object with parameters speed (the animation speed) and frames (the array representing the animation sequence).
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ The particular animation frame to display during this step.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.decideFrameOnce(cnt, anim)
+
+
+
+ Determines which frame of a given animation to display. Will remain on the last frame when the animation has played once.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Integer}
+
+
+ cnt
+
+
+ A global frame counter.
+
+
+
+
+
+ {Object}
+
+
+ anim
+
+
+ An object with parameters speed (the animation speed) and frames (the array representing the animation sequence).
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ The particular animation frame to display during this step.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.finalizeTilemap(map)
+
+
+
+ Calculates and sets the width and height (map.h, map.w) and half-width and half-height (map.hh, map.hw) of a map object.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ map
+
+
+ A map object, containing a map array and a tileset array.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ A map object with map.w, map.h, map.hh, and map.hw set correctly.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.framestotime(frames)
+
+
+
+ Converts a quantity of frames into a timestamp formatted "mm:ss:cs" (minutes, seconds, centiseconds). Calculated using the current frames per second.
+
+
+
+
+
+ A string containing a timestamp formatted "mm:ss:cs", representing the length of time it would take to render that many frames.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.getArrayCapped(a, id)
+
+
+
+ Returns the Nth element in an array. If the array is shorter than N, it returns the last element of the array.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Array}
+
+
+ a
+
+
+ An array.
+
+
+
+
+
+ {Integer}
+
+
+ id
+
+
+ An index to the array.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ If id > a.length, it returns a[a.length-1]. Otherwise returns a[id].
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.getArrayIndexed(a, value, field)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+
+
+
+ value
+
+
+
+
+
+
+
+
+
+
+
+ field
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.getDeviceConfig()
+
+
+
+ Automatically configures a bunch of settings depending on the web browser and device that is viewing the game. Mostly sets the maximum number of audio channels and touch settings.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.getTileInMap(x, y, map, ifout, mapid)
+
+
+
+ Given x,y coordinates and map information, this returns the tile at a given point.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Integer}
+
+
+ x
+
+
+ An x-coordinate.
+
+
+
+
+
+ {Integer}
+
+
+ y
+
+
+ A y-coordinate.
+
+
+
+
+
+ {Object}
+
+
+ map
+
+
+ The map object.
+
+
+
+
+
+ {Object}
+
+
+ ifout
+
+
+ An object or value to be returned if the x,y coordinate pair is outside the map.
+
+
+
+
+
+ {String}
+
+
+ mapid
+
+
+ The id for the map array within the map object. Default is 'map'.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ An integer representing the value of the tile in the map array at that x,y coordinate. If there is no tile, null is returned.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.geturlparameter(name)
+
+
+
+ Reads the value of a query parameter from the URL of the web page.
+
+
+
+
+
// If the URL is http://example.com/game.html?lives=3
player.lives = help.geturlparameter("lives");
player.lives; // => 3
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ name
+
+
+ The name of the URL parameter.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ The value of the URL parameter, as a string.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.goToZero(v)
+
+
+
+ Subtracts or adds 1 to a value, always converging to zero. For example, passing -3 yields -2, 5 yields 4, etc. Works best with integers.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Integer}
+
+
+ v
+
+
+ A value.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ A value that is one closer to 0 on the number line than v.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.isDefined(A)
+
+
+
+ Tests whether an object contains a given parameter.
+
+
+
+
+
+ True if the object contains that parameter, false if it does not.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.isLastFrameOnce(cnt, anim)
+
+
+
+ Returns whether the animation was fully played at least once with decideFrame or fully with decideFrameOnce.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Integer}
+
+
+ cnt
+
+
+ A global frame counter.
+
+
+
+
+
+ {Object}
+
+
+ anim
+
+
+ An object with parameters speed (the animation speed) and frames (the array representing the animation sequence).
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ A boolean, true if the animation has been played at least once.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.isSquished(th, by)
+
+
+
+ Tests to see if an object is being "jumped on" by another object. Only works for platformers, since it assumes accy>0 means you're falling onto something else.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ th
+
+
+ The object that is (possibly) being jumped on.
+
+
+
+
+
+ {Object}
+
+
+ by
+
+
+ The object doing the jumping-on.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ True if the two objects are overlapping enough and by.accy>0.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.limit(v, min, max)
+
+
+
+ Limits a number to a certain range. If the number is below the minimum, the minimum is returned. If the number is above the maximum, the maximum is returned.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Float}
+
+
+ v
+
+
+ A value.
+
+
+
+
+
+ {Float}
+
+
+ min
+
+
+ The minimum limit.
+
+
+
+
+
+ {Float}
+
+
+ max
+
+
+ The maximum limit.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ A value equal to v if minmax.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.mergeWithModel(data, model)
+
+
+
+ Merges two sets of parameters together without overwriting existing parameters. This merges from model to data, and if data and model share parameters, data's values remain intact.
+
+
+
+
+
+ An object containing a set of parameters, the destination of the merge.
+
+
+
+
+
+ {Object}
+
+
+ model
+
+
+ An object containing a set of parameters, the source of the merge.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ A merged model where the values of 'data' remain untouched: only new parameters and values from 'model' make it in.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.multiplier(v, mul)
+
+
+
+ Multiplies two numbers together, returning the result, unless the first parameter is less than 2, in which case it returns 1.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Float}
+
+
+ v
+
+
+ First value.
+
+
+
+
+
+ {Float}
+
+
+ mul
+
+
+ Second value.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ An integer, v*mul, unless v<2 in which case it returns 1.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.objToStr(Any)
+
+
+
+ Writes the contents of an object to a string. Useful for debugging.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ Any
+
+
+ object.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ A string containing all the contents of an object. If the object contains functions, the string will contain the code for those functions.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.postpad(str, len, pad)
+
+
+
+ Postpends a string with repeated instances of another string until it the result is greater than or equal to a desired length.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ str
+
+
+ The string you wish to modify.
+
+
+
+
+
+ {Integer}
+
+
+ len
+
+
+ The desired length of your resultant string.
+
+
+
+
+
+ {String}
+
+
+ pad
+
+
+ The string you wish to postpend to str.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ A string whose length is no greater than len+pad.length, with pad postpending str repeatedly.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.prepad(str, len, pad)
+
+
+
+ Prepends a string with repeated instances of another string until it the result is greater than or equal to a desired length.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {String}
+
+
+ str
+
+
+ The string you wish to modify.
+
+
+
+
+
+ {Integer}
+
+
+ len
+
+
+ The desired length of your resultant string.
+
+
+
+
+
+ {String}
+
+
+ pad
+
+
+ The string you wish to prepend to str.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ A string whose length is no greater than len+pad.length, with pad prepending str repeatedly.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.random(min, range)
+
+
+
+ Generates uniformly distributed random integers between min and min+range, non-inclusive. So help.random(0,2) will only return 0 and 1, etc.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Integer}
+
+
+ min
+
+
+ The minimum random value to be returned by the function.
+
+
+
+
+
+ {Integer}
+
+
+ range
+
+
+ The number of different values returned by the function.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ An integer between min (includive) and min+range (noninclusive).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.seq(st, ed, skip)
+
+
+
+ Generates numbers from st to ed, along with a skip value.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Integer}
+
+
+ st
+
+
+ Starting number.
+
+
+
+
+
+ {Integer}
+
+
+ ed
+
+
+ Ending number.
+
+
+
+
+
+ {Integer}
+
+
+ skip
+
+
+ Number to increment by.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ An array containing the set of numbers from st to ed, incrementing by skip.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.setTileInMap(ctx, map, x, y, tile, The)
+
+
+
+ Sets a tile in the map and draws it. Does not return anything.
+
+
+
+
+
// Remove the second tile to the right and down from the upper left corner of the tile map. Assumes our map canvas is called 'map_canvas'.
help.setTileInMap(gbox.getCanvasContext("map_canvas"),map,1,1,null,"map");
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ ctx
+
+
+ The canvas context for the map. Accessed via gbox.getCanvasContext("canvasname")
+
+
+
+
+
+ {Object}
+
+
+ map
+
+
+ The game map object.
+
+
+
+
+
+ {Integer}
+
+
+ x
+
+
+ The index of the tile column within the map array -- so a 1 would mean the second column of tiles.
+
+
+
+
+
+ {Integer}
+
+
+ y
+
+
+ The index of the tile row within the map array -- so a 1 would mean the second row of tiles.
+
+
+
+
+
+ {Integer}
+
+
+ tile
+
+
+ The integer representing the new tile you wish to draw. This is its index within the tileset; a null value will erase whatever tile is present.
+
+
+
+
+
+ {String}
+
+
+ The
+
+
+ ID of the map. Defaults to 'map'.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.upAndDown(counter, max)
+
+
+
+ Given an incrementing value each step, this will return a value increasing from 0 until max/2, at which point it will decrement to 0, then go back up to max/2, in an endless cycle.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Integer}
+
+
+ counter
+
+
+ A counter.
+
+
+
+
+
+ {Integer}
+
+
+ max
+
+
+ This determines the period of the function -- assuming counter is incrementing by one, a complete back-and-forth will take 'max' steps.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ An integer.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.xPixelToTile(map, x, gap)
+
+
+
+ Converts an x-coordinate of a pixel to the x-coordinate of the tile column it's in. This effectively "snaps" an x coordinate to a tile edge.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ map
+
+
+ A map object, containing a map array and a tileset array.
+
+
+
+
+
+ {Integer}
+
+
+ x
+
+
+ An x-coordinate.
+
+
+
+
+
+ {Integer}
+
+
+ gap
+
+
+ Number of pixels gap in tilemap. Default is 0.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ The x-coordinate in pixels of the tile column.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.xPixelToTileX(map, x, gap)
+
+
+
+ Converts an x-coordinate of a pixel to its corresponding tile x-coordinate.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ map
+
+
+ A map object, containing a map array and a tileset array.
+
+
+
+
+
+ {Integer}
+
+
+ x
+
+
+ An x-coordinate.
+
+
+
+
+
+ {Integer}
+
+
+ gap
+
+
+ (Not used.)
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ A map object with map.w, map.h, map.hh, and map.hw set correctly.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.yPixelToTile(map, y, gap)
+
+
+
+ Converts a y-coordinate of a pixel to the y-coordinate of the tile row it's in. This effectively "snaps" a y coordinate to a tile edge.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ map
+
+
+ A map object, containing a map array and a tileset array.
+
+
+
+
+
+ {Integer}
+
+
+ y
+
+
+ A y-coordinate.
+
+
+
+
+
+ {Integer}
+
+
+ gap
+
+
+ Number of pixels gap in tilemap. Default is 0.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ The y-coordinate in pixels of the tile row.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ help.yPixelToTileY(map, y, gap)
+
+
+
+ Converts a y-coordinate of a pixel to its corresponding tile y-coordinate.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ map
+
+
+ A map object, containing a map array and a tileset array.
+
+
+
+
+
+ {Integer}
+
+
+ y
+
+
+ A y-coordinate.
+
+
+
+
+
+ {Integer}
+
+
+ gap
+
+
+ (Not used.)
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ A map object with map.w, map.h, map.hh, and map.hw set correctly.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation generated by JsDoc Toolkit 2.3.2 on Wed Aug 18 2010 16:09:24 GMT-0400 (EDT)
+
1// ---
+ 2// Copyright (c) 2010 Francesco Cottone, http://www.kesiev.com/
+ 3// ---
+ 4
+ 5/**
+ 6 * @namespace
+ 7 * Gamecycle contains your basic game loop: intro, menus,
+ 8 * crossfading between stages/lifes, gameover and ending.
+ 9 */
+ 10vargamecycle={
+ 11
+ 12/**
+ 13 * Gamecycle constructor - initializes a new game object
+ 14 *
+ 15 * @param id unique id of object
+ 16 * @param group name of group to store the object in
+ 17 */
+ 18createMaingame:function(id,group){
+ 19returngbox.addObject({
+ 20id:id,
+ 21group:group,
+ 22counter:0,
+ 23difficulty:0,
+ 24
+ 25// state transition
+ 26state:50,
+ 27stateFirstIteration:true,
+ 28
+ 29hud:{},
+ 30
+ 31/**
+ 32 * This method is called whenever you load a new map. It's meant to be
+ 33 * overridden when you create your game.
+ 34 */
+ 35changeLevel:function(){},
+ 36
+ 37/**
+ 38 * This method is called every time a player is "reborn". This method is
+ 39 * meant to be overridden since you have to do garbage collection.
+ 40 */
+ 41newLife:function(){},
+ 42
+ 43// game disclaimer animation (if needed)
+ 44gameDisclaimerAnimation:function(reset){
+ 45returntrue;
+ 46},
+ 47
+ 48// game intro animation
+ 49gameIntroAnimation:function(reset){
+ 50if(reset){
+ 51gbox.stopChannel("bgmusic");
+ 52toys.resetToy(this,"default-blinker");
+ 53}else{
+ 54gbox.blitFade(gbox.getBufferContext(),{alpha:1});
+ 55returntoys.text.blink(this,"default-blinker",gbox.getBufferContext(),{font:"small",text:"LETS BEGIN!",valign:gbox.ALIGN_MIDDLE,halign:gbox.ALIGN_CENTER,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH(),blinkspeed:5,times:6});
+ 56}
+ 57},
+ 58
+ 59// level intro animation
+ 60levelIntroAnimation:function(reset){
+ 61if(reset){
+ 62gbox.stopChannel("bgmusic");
+ 63toys.resetToy(this,"default-blinker");
+ 64}else{
+ 65gbox.blitFade(gbox.getBufferContext(),{alpha:1});
+ 66returntoys.text.blink(this,"default-blinker",gbox.getBufferContext(),{font:"small",text:"GET READY!",valign:gbox.ALIGN_MIDDLE,halign:gbox.ALIGN_CENTER,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH(),blinkspeed:5,times:6});
+ 67}
+ 68},
+ 69
+ 70// Life intro animation
+ 71newlifeIntroAnimation:function(reset){
+ 72if(reset){
+ 73gbox.stopChannel("bgmusic");
+ 74toys.resetToy(this,"default-blinker");
+ 75}else{
+ 76gbox.blitFade(gbox.getBufferContext(),{alpha:1});
+ 77returntoys.text.fixed(this,"default-blinker",gbox.getBufferContext(),{font:"small",text:"GET READY!",valign:gbox.ALIGN_MIDDLE,halign:gbox.ALIGN_CENTER,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH(),time:30});
+ 78}
+ 79},
+ 80
+ 81// gameover animation
+ 82gameoverIntroAnimation:function(reset){
+ 83if(reset){
+ 84gbox.stopChannel("bgmusic");
+ 85toys.resetToy(this,"default-blinker");
+ 86}else{
+ 87gbox.blitFade(gbox.getBufferContext(),{alpha:1});
+ 88returntoys.text.fixed(this,"default-blinker",gbox.getBufferContext(),{font:"small",text:"GAME OVER",valign:gbox.ALIGN_MIDDLE,halign:gbox.ALIGN_CENTER,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH(),time:50});
+ 89}
+ 90},
+ 91
+ 92// game title animation
+ 93gameTitleIntroAnimation:function(reset){
+ 94if(reset)
+ 95gbox.stopChannel("bgmusic");
+ 96else{
+ 97gbox.blitFade(gbox.getBufferContext(),{alpha:1});
+ 98gbox.blitText(gbox.getBufferContext(),{font:"small",text:"GAME TITLE",valign:gbox.ALIGN_MIDDLE,halign:gbox.ALIGN_CENTER,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH()-100});
+ 99}
+100},
+101
+102// End level animation
+103endlevelIntroAnimation:function(reset){
+104if(reset){
+105toys.resetToy(this,"default-blinker");
+106}else{
+107returntoys.text.blink(this,"default-blinker",gbox.getBufferContext(),{font:"small",text:"WELL DONE!",valign:gbox.ALIGN_MIDDLE,halign:gbox.ALIGN_CENTER,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH(),blinkspeed:5,times:10});
+108}
+109},
+110
+111// Game ending
+112gameEndingIntroAnimation:function(reset){
+113if(reset){
+114toys.resetToy(this,"default-blinker");
+115}else{
+116gbox.blitFade(gbox.getBufferContext(),{alpha:1});
+117returntoys.text.blink(this,"default-blinker",gbox.getBufferContext(),{font:"small",text:"CONGRATULATIONS!",valign:gbox.ALIGN_MIDDLE,halign:gbox.ALIGN_CENTER,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH(),blinkspeed:5,times:10});
+118}
+119},
+120
+121// PRESS START
+122pressStartIntroAnimation:function(reset){
+123if(reset){
+124toys.resetToy(this,"default-blinker");
+125}else{
+126toys.text.blink(this,"default-blinker",gbox.getBufferContext(),{font:"small",text:"PRESS A TO START",valign:gbox.ALIGN_MIDDLE,halign:gbox.ALIGN_CENTER,dx:0,dy:Math.floor(gbox.getScreenH()/3),dw:gbox.getScreenW(),dh:Math.floor(gbox.getScreenH()/3)*2,blinkspeed:10});
+127returngbox.keyIsHit("a");
+128}
+129},
+130
+131/**
+132 * This method is called when the player dies.
+133 */
+134gameIsOver:function(){returntrue;},
+135
+136/**
+137 * Actions done during the game (i.e. stage is clear or other ending conditions)
+138 */
+139gameEvents:function(){
+140
+141},
+142
+143gameMenu:function(reset){
+144if(reset){
+145toys.resetToy(this,"difficulty");
+146}else{
+147gbox.blitFade(gbox.getBufferContext(),{alpha:0.5});
+148if(toys.ui.menu(this,"difficulty",{audiooption:"default-menu-option",audioconfirm:"default-menu-confirm",font:"small",keys:{up:"up",down:"down",ok:"a",cancel:"b"},selector:">",items:["EASY","NORMAL","HARD"],x:10,y:10})){
+149if(toys.getToyValue(this,"difficulty","ok")==-1)return-1;
+150else{
+151this.difficulty=toys.getToyValue(this,"difficulty","selected");
+152returntrue;
+153}
+154}
+155returnfalse;
+156}
+157},
+158
+159// CHECK
+160
+161gameIsHold:function(){// Use this clause to check collision and kill player: if true the level is changing
+162return(this.state==400)||(this.state==401);
+163},
+164
+165isCompleted:function(){
+166return(this.state==800);
+167},
+168
+169// GAME CYCLE
+170
+171getNextLevel:function(){
+172returnthis._nextlevel;
+173},
+174
+175gotoLevel:function(level){
+176this._nextlevel=level;
+177this.setState(400);
+178},
+179
+180playerDied:function(data){
+181this._loselife=data;
+182this.setState(500);
+183},
+184
+185gameIsCompleted:function(){
+186this.setState(800);
+187},
+188
+189// private methods
+190
+191/**
+192 * Changes the current game state
+193 *
+194 * @param st state number
+195 */
+196setState:function(st){
+197this.state=st;
+198this.stateFirstIteration=true;
+199},
+200
+201/*
+202 * Removes all objects in each group except the game
+203 * cycle group. Used for garbage collection when resetting the game.
+204 */
+205_resetGroups:function(){
+206varg=gbox.getGroups();
+207for(vari=0;i<g.length;i++)
+208if(g[i]!=this.group)gbox.clearGroup(g[i]);
+209gbox.soloGroup(this.group);
+210},
+211
+212stateIsReady:function(){this.stateFirstIteration=false;},
+213
+214blit:function(){
+215switch(this.state){
+216
+217// Disclaimer
+218case50:{
+219if(this.stateFirstIteration){
+220this._resetGroups();
+221this.gameDisclaimerAnimation(true);
+222this.stateIsReady();
+223}
+224if(this.gameDisclaimerAnimation(false))
+225this.setState(100);
+226break;
+227}
+228// main menu
+229case100:
+230case101:
+231case102:{// Press Start / Menu
+232if(this.stateFirstIteration&&(this.state==100)){
+233this._resetGroups();
+234this.gameTitleIntroAnimation(true);
+235}
+236this.gameTitleIntroAnimation(false);
+237switch(this.state){
+238case100:{// Press to start
+239if(this.stateFirstIteration){
+240gbox.resetChannel("bgmusic");
+241this.pressStartIntroAnimation(true);
+242this.stateIsReady();
+243}
+244if(this.pressStartIntroAnimation(false))this.setState(101);
+245break;
+246}
+247case101:{// Game menu
+248if(this.stateFirstIteration){
+249gbox.setChannelVolume("bgmusic",0.5);
+250this.gameMenu(true);
+251this.stateIsReady();
+252}
+253
+254varmenu=this.gameMenu(false);
+255if(menu)
+256if(menu==-1)this.setState(100);elsethis.setState(102);
+257break;
+258}
+259case102:{// Fader
+260if(this.stateFirstIteration){
+261this._resetGroups();
+262toys.resetToy(this,"fadeout");
+263this.stateIsReady();
+264}
+265if(toys.fullscreen.fadeout(this,"fadeout",gbox.getBufferContext(),{fadespeed:0.05,audiochannelfade:"bgmusic"}))
+266this.setState(200);
+267break;
+268}
+269break;
+270}
+271break;
+272}
+273case200:// Game intro animation
+274
+275case300:// Start game
+276case301:// Game is going
+277
+278case400:// Fade out to change level
+279case401:// Level animation (levelIntroAnimation)
+280case402:// Fade in to next level
+281
+282case500:// Wait after dead
+283case501:// Dead fadeout
+284
+285case600:// Next life intro
+286case601:// New life fadein
+287
+288case700:// Gameover animation
+289
+290case800:// Fade out game ending
+291case801:// Game ending
+292
+293{// Game playing
+294if(this.stateFirstIteration){
+295switch(this.state){
+296case200:{// Game intro
+297toys.resetToy(this,"fadein");
+298this.level=null;
+299this._nextlevel=null;
+300this.hud=toys.ui.hud("maingamehud");
+301
+302this.initializeGame();
+303this.gameIntroAnimation(true);
+304
+305break;
+306}
+307case300:{
+308// Game start
+309this.level=this._nextlevel;
+310gbox.playAllGroups();
+311this.changeLevel(this._nextlevel);
+312}
+313case800:
+314case400:{
+315this.endlevelIntroAnimation(true);
+316toys.resetToy(this,"fadeout");
+317break;
+318}
+319case501:{
+320toys.resetToy(this,"fadeout");
+321break;
+322}
+323case401:{
+324gbox.soloGroup(this.group);
+325this.levelIntroAnimation(true);
+326break;
+327}
+328case402:{
+329toys.resetToy(this,"fadein");
+330this.level=this._nextlevel;
+331gbox.playAllGroups();
+332this.changeLevel(this._nextlevel);
+333break;
+334}
+335case600:{
+336gbox.soloGroup(this.group);
+337this.newlifeIntroAnimation(true);
+338break;
+339}
+340case500:{
+341this._loselife.counter=0;
+342break;
+343}
+344case601:{
+345toys.resetToy(this,"fadein");
+346this.newLife();
+347gbox.playAllGroups();
+348break;
+349}
+350case700:{
+351gbox.resetChannel("bgmusic");
+352gbox.soloGroup(this.group);
+353this.gameoverIntroAnimation(true);
+354break;
+355}
+356case801:{
+357gbox.resetChannel("bgmusic");
+358gbox.soloGroup(this.group);
+359this.gameEndingIntroAnimation(true);
+360break;
+361}
+362}
+363this.stateIsReady();
+364}
+365
+366switch(this.state){
+367case200:{// Game intro
+368if(this.gameIntroAnimation(false))this.setState(300);
+369break;
+370}
+371case601:// Fade in with new life
+372case402:// Fade in after level change
+373case300:{// Fade in at the beginning of the game
+374if(toys.fullscreen.fadein(this,"fadein",gbox.getBufferContext(),{fadespeed:0.05,audiochannelfade:"bgmusic"}))this.setState(301);
+375break;
+376}
+377case301:{// Ingame stuff
+378this.gameEvents();
+379break;
+380}
+381case400:{// Fade out before changing the level
+382if(this.endlevelIntroAnimation(false))
+383if(toys.fullscreen.fadeout(this,"fadeout",gbox.getBufferContext(),{fadespeed:0.05,audiochannelfade:"bgmusic"}))this.setState(401);
+384break;
+385}
+386case800:{// Fade out before game ending
+387if(this.endlevelIntroAnimation(false))
+388if(toys.fullscreen.fadeout(this,"fadeout",gbox.getBufferContext(),{fadespeed:0.05,audiochannelfade:"bgmusic"}))this.setState(801);
+389break;
+390}
+391case501:{// Fade out after dead
+392if(toys.fullscreen.fadeout(this,"fadeout",gbox.getBufferContext(),{fadespeed:0.05,audiochannelfade:"bgmusic"}))
+393if(this.gameIsOver())
+394this.setState(700);// GAME OVER
+395else
+396this.setState(600);
+397break;
+398}
+399case401:{// Level intro animation
+400if(this.levelIntroAnimation(false))this.setState(402);
+401break;
+402}
+403case500:{// Wait after dead
+404this._loselife.counter++;
+405if(this._loselife.counter==this._loselife.wait)this.setState(501);
+406break;
+407}
+408case600:{// New life intro
+409if(this.newlifeIntroAnimation(false))this.setState(601);
+410break;
+411}
+412case700:{// gameover
+413if(this.gameoverIntroAnimation(false))this.setState(100);// Restart game
+414break;
+415}
+416case801:{// Game ending
+417if(this.gameEndingIntroAnimation(false)){
+418this._loselife={ending:true};
+419this.setState(700);// Game over
+420}
+421break;
+422}
+423}
+424this.hud.blit();
+425break;
+426}
+427}
+428}
+429});
+430}
+431}
+432
\ No newline at end of file
diff --git a/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_gbox.js.html b/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_gbox.js.html
new file mode 100644
index 0000000..07d8319
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_gbox.js.html
@@ -0,0 +1,1856 @@
+
1// ---
+ 2// Copyright (c) 2010 Francesco Cottone, http://www.kesiev.com/
+ 3// ---
+ 4
+ 5vardynalist={
+ 6create:function(){
+ 7return{
+ 8first:null,
+ 9last:null,
+ 10data:[],
+ 11dl:0,
+ 12gar:[],
+ 13disconnect:function(obd){
+ 14if(this.data[obd].__first!=null)this.data[this.data[obd].__first].__next=this.data[obd].__next;elsethis.first=this.data[obd].__next;
+ 15if(this.data[obd].__next!=null)this.data[this.data[obd].__next].__first=this.data[obd].__first;elsethis.last=this.data[obd].__first;
+ 16},
+ 17addObject:function(obj,prio){
+ 18varnid=this.gar.pop();
+ 19if(nid==null){
+ 20nid=this.dl;
+ 21this.dl++;
+ 22}
+ 23if(this.first==null){// First element
+ 24obj.__next=null;
+ 25obj.__first=null;
+ 26this.first=nid;
+ 27this.last=nid;
+ 28}else{// Chain next
+ 29vari=this.first;
+ 30while(i!=null)
+ 31if(this.data[i].__prio>prio)break;elsei=this.data[i].__next;
+ 32if(i==null){// if last, chain in queue
+ 33obj.__next=null;
+ 34obj.__first=this.last;
+ 35this.data[this.last].__next=nid;
+ 36this.last=nid;
+ 37}else{// else reconnect objects
+ 38obj.__first=this.data[i].__first;
+ 39obj.__next=i;
+ 40this.data[i].__first=nid;
+ 41if(obj.__first!=null)this.data[obj.__first].__next=nid;elsethis.first=nid;
+ 42}
+ 43
+ 44}
+ 45obj.__prio=prio;
+ 46obj.__id=nid;
+ 47this.data[nid]=obj;
+ 48returnnid;
+ 49},
+ 50setPrio:function(obd,prio){
+ 51if(this.data[obd].__prio==prio)return;
+ 52if(this.first!=this.last)
+ 53if(this.data[obd].__prio<prio){
+ 54if(this.data[obd].__id!=this.last){
+ 55vari=this.data[obd].__next;
+ 56while(i!=null)
+ 57if(this.data[i].__prio>=prio)break;elsei=this.data[i].__next;
+ 58if((i==null)||(this.data[i].__first!=this.data[obd].__id)){
+ 59// disconnect
+ 60this.disconnect(obd);
+ 61// Reconnect
+ 62if(i==null){
+ 63this.data[this.last].__next=this.data[obd].__id;
+ 64this.data[obd].__first=this.last;
+ 65this.data[obd].__next=null;
+ 66this.last=this.data[obd].__id;
+ 67}else{
+ 68this.data[obd].__first=this.data[i].__first;
+ 69this.data[obd].__next=i;
+ 70this.data[i].__first=this.data[obd].__id;
+ 71if(this.data[obd].__first!=null)this.data[this.data[obd].__first].__next=this.data[obd].__id;elsethis.first=this.data[obd].__id;
+ 72}
+ 73}
+ 74}
+ 75}else{
+ 76if(this.data[obd].__id!=this.first){
+ 77vari=this.data[obd].__first;
+ 78while(i!=null)
+ 79if(this.data[i].__prio<=prio)break;elsei=this.data[i].__first;
+ 80if((i==null)||(this.data[i].__next!=this.data[obd].__id)){
+ 81// disconnect
+ 82this.disconnect(obd);
+ 83if(i==null){
+ 84this.data[this.first].__first=this.data[obd].__id;
+ 85this.data[obd].__first=null;
+ 86this.data[obd].__next=this.first;
+ 87this.first=this.data[obd].__id;
+ 88}else{
+ 89this.data[obd].__first=i;
+ 90this.data[obd].__next=this.data[i].__next;
+ 91this.data[i].__next=this.data[obd].__id;
+ 92if(this.data[obd].__next!=null)this.data[this.data[obd].__next].__first=this.data[obd].__id;elsethis.last=this.data[obd].__id;
+ 93}
+ 94}
+ 95}
+ 96}
+ 97this.data[obd].__prio=prio;
+ 98},
+ 99remove:function(obd){
+100this.disconnect(obd);
+101this.gar.push(this.data[obd].__id);
+102deletethis.data[this.data[obd].__id];
+103}
+104}
+105}
+106}
+107
+108// A special circular queue with some features useful for the resource loader
+109varcyclelist={
+110create:function(size){
+111return{
+112_head:0,
+113_tail:0,
+114_data:[],
+115_size:(size?size:10),
+116_total:0,
+117_done:0,
+118_current:null,
+119getTotal:function(){returnthis._total},// Number of elements to be "poped"
+120getDone:function(){returnthis._done},// Number of popped elements since the last empty
+121getSize:function(){returnthis._size},// The maximum number of elements in the queue
+122isProcessing:function(){returnthis._current!=null},// The last pop was not a null (i.e. the queue returned a valid object)
+123isEnded:function(){return(this._head==this._tail)},// There are other elements in the queue
+124isBusy:function(){returnthis.isProcessing()||!this.isEnded()},// There are elements in the queue/the last one pop returned an object that is being processed
+125getCurrent:function(){returnthis._current},// Return the last popped element
+126push:function(d){
+127this._data[this._head]=d;
+128this._head=(this._head+1)%this._size;
+129this._total++;
+130},
+131pop:function(){
+132if(this.isEnded()){
+133this._total=0;
+134this._done=0;
+135this._current=null;
+136}else{
+137this._current=this._data[this._tail];
+138this._tail=(this._tail+1)%this._size;
+139this._done++;
+140}
+141returnthis._current;
+142},
+143dump:function(){
+144varr="";
+145for(vari=0;i<this._size;i++){
+146r+=i+") "+this._data[i]+" | "+(i==this._head?"HEAD ":"")+(i==this._tail?"TAIL ":"")+"\n";
+147}
+148r+="\n\n"+this._done+"/"+this._total;
+149returnr;
+150}
+151}
+152}
+153}
+154
+155// A simple circular cache handler
+156varcachelist={
+157create:function(size){
+158return{
+159_cache:{},
+160_queue:[],
+161_head:0,
+162_size:(size?size:10),
+163add:function(k,v){
+164if(!this._cache[k]){
+165if(this._queue[this._head])
+166deletethis._cache[this._queue[this._head]];
+167this._queue[this._head]=k;
+168this._cache[k]={pos:this._head,value:v};
+169this._head=(this._head+1)%this._size;
+170}elsethis._cache[k].value=v;
+171},
+172read:function(k){
+173return(this._cache[k]?this._cache[k].value:null);
+174},
+175clear:function(){
+176this._cache={};
+177this._queue=[];
+178this._head=0;
+179}
+180}
+181}
+182}
+183
+184/**
+185 * @namespace
+186 * Gamebox module allows multiple grouped objects to move simultaneously, it helps with collisions,
+187 * rendering and moving objects. It also provides monospaced pixel-font rendering, keyboard handling,
+188 * audio, double buffering and eventually FSEs. Gamebox can also store and load data from cookies!
+189 */
+190vargbox={
+191// CONSTANTS
+192ALIGN_CENTER:0,
+193ALIGN_MIDDLE:0,
+194ALIGN_RIGHT:1,
+195ALIGN_BOTTOM:1,
+196COLOR_BLACK:'rgb(0,0,0)',
+197COLOR_WHITE:'rgb(255,255,255)',
+198ZINDEX_LAYER:-1,
+199
+200// VARS
+201_autoid:0,
+202_cb:null,// callback for loadAll()
+203_keyboard:[],
+204_keymap:{
+205up:38,
+206down:40,
+207right:39,
+208left:37,
+209a:90,
+210b:88,
+211c:67
+212},
+213_flags:{
+214experimental:false,
+215noaudio:false
+216},
+217_fonts:{},
+218_tiles:{},
+219_images:{},
+220_camera:{},
+221_screen:0,
+222_screenposition:0,
+223_keyboardpicker:0,
+224_screenh:0,
+225_screenw:0,
+226_screenhh:0,
+227_screenhw:0,
+228_zoom:1,
+229_canvas:{},
+230_objects:{},
+231_groups:[],
+232_renderorder:[],
+233_groupplay:{},
+234_actionqueue:["first","then","blit","after"],// initialize is executed once
+235_mspf:0,
+236_fps:0,
+237_gametimer:0,
+238_frameskip:0,
+239_autoskip:{min:0,max:5,lowidle:0,hiidle:5},// minimum frameskip, maximum frameskip, minimum idle time allowed for increasing frameskip, maximum idle time allowed for decreasing frameskip
+240_fskid:0,
+241_statbar:0,
+242_border:0,
+243_garbage:[],
+244_zindexch:[],
+245_framestart:0,
+246_zindex:dynalist.create(),
+247_db:false,
+248_systemcookie:"__gboxsettings",
+249_sessioncache:"",
+250_breakcacheurl:function(a){returna+(a.indexOf("?")==-1?"?":"&")+"_brc="+gbox._sessioncache;},
+251_forcedidle:0,
+252_gamewaiting:0,
+253_canlog:false,
+254_splash:{
+255gaugeLittleColor:"rgb(255,240,0)",
+256gaugeLittleBackColor:"rgb(255,255,255)",
+257gaugeBorderColor:"rgb(0,0,0)",
+258gaugeBackColor:"rgb(100,100,100)",
+259gaugeColor:"rgb(255,240,0)",
+260gaugeHeight:10,
+261background:null,
+262minimalTime:0,
+263footnotes:null,
+264footnotesSpacing:1
+265},
+266_minimalexpired:0,// 0: not triggered, 1: triggered, 2: done
+267setCanLog:function(c){this._canlog=c&&window.console;},
+268canLog:function(){returnthis._canlog},
+269log:function(){},// Overridden if console is really available
+270_safedrawimage:function(tox,img,sx,sy,sw,sh,dx,dy,dw,dh){
+271if(!img||!tox)return;
+272if(sx<0){dx-=(dw/sw)*sx;sw+=sx;sx=0;}
+273if(sy<0){dy-=(dh/sh)*sy;sh+=sy;sy=0;}
+274if(sx+sw>img.width){dw=(dw/sw)*(img.width-sx);sw=img.width-sx;}
+275if(sy+sh>img.height){dh=(dh/sh)*(img.height-sy);sh=img.height-sy;}
+276try{if((sh>0)&&(sw>0)&&(sx<img.width)&&(sy<img.height))tox.drawImage(img,sx,sy,sw,sh,dx,dy,dw,dh);}catch(e){}
+277},
+278_keydown:function(e){
+279varkey=(e.fake||window.event?e.keyCode:e.which);
+280if(!gbox._keyboard[key])gbox._keyboard[key]=1;
+281},
+282_keyup:function(e){
+283varkey=(e.fake||window.event?e.keyCode:e.which);
+284gbox._keyboard[key]=-1;
+285},
+286_resetkeys:function(){
+287for(varkeyingbox._keymap)
+288gbox._keyup({fake:1,keyCode:gbox._keymap[key]});
+289},
+290_showkeyboardpicker:function(){
+291gbox._keyboardpicker.value="Click/Tap here to enable the keyboard";
+292gbox._keyboardpicker.style.left=(gbox._screenposition.x+5)+"px";
+293gbox._keyboardpicker.style.top=(gbox._screenposition.y+5)+"px";
+294gbox._keyboardpicker.style.width=(gbox._screenposition.w-10)+"px";
+295gbox._keyboardpicker.style.height="30px";
+296this._keyboardpicker.style.border="1px dashed white";
+297this._keyboardpicker.readOnly=null;
+298},
+299_hidekeyboardpicker:function(){
+300this._keyboardpicker.style.zIndex=100;
+301this._keyboardpicker.readOnly="yes";
+302this._keyboardpicker.style.position="absolute";
+303this._keyboardpicker.style.textAlign="center";
+304this._keyboardpicker.style.backgroundColor="#000000";
+305this._keyboardpicker.style.color="#fefefe";
+306this._keyboardpicker.style.cursor="pointer";
+307this._keyboardpicker.value="";
+308this._keyboardpicker.style.left="0px";
+309this._keyboardpicker.style.top="0px";
+310this._keyboardpicker.style.height="0px";
+311this._keyboardpicker.style.width="0px";
+312this._keyboardpicker.style.border="0px";
+313this._keyboardpicker.style.padding="0px";
+314this._keyboardpicker.style.margin="0px";
+315},
+316_domgetabsposition:function(oElement){
+317varsizes={x:0,y:0,h:0,w:0};
+318sizes.h=oElement.offsetHeight;
+319sizes.w=oElement.offsetWidth;
+320while(oElement!=null){
+321sizes.y+=oElement.offsetTop;
+322sizes.x+=oElement.offsetLeft;
+323oElement=oElement.offsetParent;
+324}
+325returnsizes;
+326},
+327
+328/**
+329 * Sets the gbox._forcedidle property.
+330 * @param {Boolean} f The value to write to gbox._forcedidle.
+331 */
+332setForcedIdle:function(f){this._forcedidle=f},
+333
+334/**
+335 * Returns a gbox flag at index f.
+336 * @param {Object} f The index of the flag you want returned.
+337 */
+338getFlag:function(f){returnthis._flags[f]},
+339
+340/**
+341 * Sets the gbox._statbar property. Only useful if called before gbox.initScreen. Debugging funtionality.
+342 * Much easier to access if you add '?statusbar=1' to your URL.
+343 * @param {Boolean} f The value to write to gbox._statbar.
+344 */
+345setStatusBar:function(a){this._statbar=a},
+346setScreenBorder:function(a){this._border=a},
+347
+348/**
+349 * Initializes the screen to a certain width and height, applies zoom attributes, populates the
+350 * body of the HTML document including the canvas element, sets an initial camera, creates a '_buffer'
+351 * canvas, sets keyboard event listeners, and many other initialization functions.
+352 * @param {Integer} w The width of the main canvas.
+353 * @param {Integer} h The height of the main canvas.
+354 */
+355initScreen:function(w,h){
+356document.body.style.textAlign="center";
+357varcontainer=document.createElement("div");
+358container.style.width="100%";
+359container.style.height="100%";
+360container.style.display="table";
+361this._box=document.createElement("div");
+362this._box.style.display="table-cell";
+363this._box.style.width="100%";
+364this._box.style.textAlign="center";
+365this._box.style.verticalAlign="middle";
+366
+367this._screen=document.createElement("canvas");
+368if(this._border)this._screen.style.border="1px solid black";
+369this._screen.setAttribute('height',h);
+370this._screen.setAttribute('width',w);
+371this._screen.style.width=(w*this._zoom)+"px";
+372this._screen.style.height=(h*this._zoom)+"px";
+373this._screenh=h;
+374this._screenw=w;
+375this._screenhh=Math.floor(h/2);
+376this._screenhw=Math.floor(w/2);
+377this._camera.x=0;
+378this._camera.y=0;
+379this._camera.h=h;
+380this._camera.w=w;
+381this._box.appendChild(this._screen);
+382container.appendChild(this._box);
+383document.body.appendChild(container);
+384
+385this.createCanvas("_buffer");
+386window.addEventListener('keydown',this._keydown,false);
+387window.addEventListener('keyup',this._keyup,false);
+388if(this._statbar){
+389this._statbar=document.createElement("div");
+390if(this._border)this._statbar.style.border="1px solid black";
+391this._statbar.style.margin="auto";
+392this._statbar.style.backgroundColor="#ffffff";
+393this._statbar.style.fontSize="10px";
+394this._statbar.style.fontFamily="sans-serif";
+395this._statbar.style.width=(w*this._zoom)+"px";
+396this._box.appendChild(this._statbar);
+397}
+398// Keyboard support on devices that needs focus (like iPad) - actually is not working for a bug on WebKit's "focus" command.
+399this._keyboardpicker=document.createElement("input");
+400this._keyboardpicker.onclick=function(evt){gbox._hidekeyboardpicker();evt.preventDefault();evt.stopPropagation();};
+401this._hidekeyboardpicker(this._keyboardpicker);
+402
+403gbox._box.appendChild(this._keyboardpicker);
+404gbox._screen.ontouchstart=function(evt){gbox._screenposition=gbox._domgetabsposition(gbox._screen);if(evt.touches[0].pageY-gbox._screenposition.y<30)gbox._showkeyboardpicker();elsegbox._hidekeyboardpicker();evt.preventDefault();evt.stopPropagation();};
+405gbox._screen.ontouchend=function(evt){evt.preventDefault();evt.stopPropagation();};
+406gbox._screen.ontouchmove=function(evt){evt.preventDefault();evt.stopPropagation();};
+407gbox._screen.onmousedown=function(evt){gbox._screenposition=gbox._domgetabsposition(gbox._screen);if(evt.pageY-gbox._screenposition.y<30)gbox._showkeyboardpicker();elsegbox._hidekeyboardpicker();evt.preventDefault();evt.stopPropagation();};
+408
+409vard=newDate();
+410gbox._sessioncache=d.getDate()+"-"+d.getMonth()+"-"+d.getFullYear()+"-"+d.getHours()+"-"+d.getMinutes()+"-"+d.getSeconds();
+411
+412gbox._loadsettings();// Load default configuration
+413gbox.setCanAudio(true);// Tries to enable audio by default
+414},
+415
+416/**
+417 * Sets the gbox._db property. Turns on an off double buffering.
+418 * @param {Boolean} db The value to write to gbox._db. True enables double buffering, false disables.
+419 */
+420setDoubleBuffering:function(db){this._db=db},
+421
+422/**
+423 * Writes text to the status bar, but only if the status bar is enabled.
+424 * @param {String} txt The text to write to the status bar.
+425 */
+426setStatBar:function(txt){if(gbox._statbar)this._statbar.innerHTML=(txt?txt:" ")},
+427
+428/**
+429 * Set the frames per second rate.
+430 * @param {Integer} f Total frames per second for the game to run at.
+431 */
+432setFps:function(f){
+433this._fps=f;
+434this._mspf=Math.floor(1000/f)
+435},
+436
+437/**
+438 * Get the frames per second rate (default is 25).
+439 * @returns {Integer} Returns the frames per second.
+440 */
+441getFps:function(){returnthis._fps},
+442setAutoskip:function(f){this._autoskip=f},
+443setFrameskip:function(f){this._frameskip=f},
+444
+445/**
+446 * Get the screen height.
+447 * @returns {Integer} Screen height in pixels.
+448 */
+449getScreenH:function(){returnthis._screenh},
+450
+451/**
+452 * Get the screen width.
+453 * @returns {Integer} Screen width in pixels.
+454 */
+455getScreenW:function(){returnthis._screenw},
+456
+457/**
+458 * Get the screen half-height.
+459 * @returns {Integer} Screen half-height in pixels.
+460 */
+461getScreenHH:function(){returnthis._screenhh},
+462
+463/**
+464 * Get the screen half-width.
+465 * @returns {Integer} Screen half-width in pixels.
+466 */
+467getScreenHW:function(){returnthis._screenhw},
+468
+469/**
+470 * Sets the gbox._zoom parameter, only works before gbox.initScreen is called.
+471 * @param {Integer} z Zoom factor.
+472 */
+473setZoom:function(z){this._zoom=z},
+474
+475/**
+476 * Deprecated: gbox._cb is now set by passing it directly into gbox.loadAll(). Left in for backwards compatibility.
+477 * @param {String} cb The name of the function to be called once gbox.loadAll is completed.
+478 */
+479setCallback:function(cb){this._cb=cb;},
+480
+481_playobject:function(g,obj,a){
+482if(gbox._objects[g][obj].initialize){
+483gbox._objects[g][obj].initialize(obj);
+484deletegbox._objects[g][obj].initialize;
+485}
+486if(gbox._objects[g][obj][a])gbox._objects[g][obj][a](obj,a);
+487},
+488
+489_nextframe:function(){
+490gbox._framestart=gbox._mspf-(newDate().getTime()-gbox._framestart);
+491if(gbox._autoskip)
+492if((gbox._framestart<gbox._autoskip.lowidle)&&(gbox._frameskip<gbox._autoskip.max))gbox.setFrameskip(gbox._frameskip+1);else
+493if((gbox._framestart>gbox._autoskip.hiidle)&&(gbox._frameskip>gbox._autoskip.min))gbox.setFrameskip(gbox._frameskip-1);
+494if(gbox._statbar)gbox.debugGetstats();
+495this._gametimer=setTimeout(gbox.go,(gbox._framestart<=0?1:gbox._framestart));
+496},
+497
+498/**
+499 * This function is called once per frame. This is where the basic rendering and processing of groups occurs.
+500 */
+501go:function(){
+502if(gbox._loaderqueue.isBusy()){
+503if(gbox._gamewaiting==1){
+504gbox.blitFade(gbox._screen.getContext("2d"),{alpha:0.5});
+505gbox.blitText(gbox._screen.getContext("2d"),{font:"_dbf",dx:2,dy:2,text:"LOADING..."});
+506gbox._gamewaiting=true;
+507}
+508if(gbox._gamewaiting<=1){
+509varbw=Math.floor(((gbox.getScreenW()-4)*gbox._loaderqueue.getDone())/gbox._loaderqueue.getSize());
+510gbox._screen.getContext("2d").globalAlpha=1;
+511gbox._screen.getContext("2d").fillStyle=gbox._splash.gaugeLittleBackColor;
+512gbox._screen.getContext("2d").fillRect(0,4+gbox.getFont("_dbf").tileh,gbox.getScreenW(),1);
+513gbox._screen.getContext("2d").fillStyle=gbox._splash.gaugeLittleColor;
+514gbox._screen.getContext("2d").fillRect(0,4+gbox.getFont("_dbf").tileh,(bw>0?bw:0),1);
+515gbox._screen.getContext("2d").restore();
+516gbox.setStatBar("Loading... ("+gbox._loaderqueue.getDone()+"/"+gbox._loaderqueue.getTotal()+")");
+517}
+518if(gbox._gamewaiting)gbox._gamewaiting--;
+519setTimeout(gbox.go,1000);
+520}else{
+521gbox._gamewaiting=3;
+522gbox._framestart=newDate().getTime();
+523vargr="";
+524for(varg=0;g<gbox._renderorder.length;g++)
+525if(gbox._groupplay[gbox._renderorder[g]])
+526if(gbox._renderorder[g]==gbox.ZINDEX_LAYER){
+527varid;
+528for(vari=0;i<gbox._actionqueue.length;i++){
+529id=gbox._zindex.first;
+530while(id!=null){
+531if(gbox._groupplay[gbox._zindex.data[id].g])
+532gbox._playobject(gbox._zindex.data[id].g,gbox._zindex.data[id].o,gbox._actionqueue[i]);
+533id=gbox._zindex.data[id].__next;
+534}
+535}
+536}else
+537for(vari=0;i<gbox._actionqueue.length;i++)
+538for(varobjingbox._objects[gbox._renderorder[g]])
+539gbox._playobject(gbox._renderorder[g],obj,gbox._actionqueue[i]);
+540if(gbox._fskid>=gbox._frameskip){
+541if(gbox._db)gbox.blitImageToScreen(gbox.getBuffer());
+542gbox._fskid=0;
+543}elsegbox._fskid++;
+544
+545gbox.purgeGarbage();
+546
+547if(gbox._zindexch.length){
+548
+549for(vari=0;i<gbox._zindexch.length;i++){
+550if(gbox._objects[gbox._zindexch[i].o.g][gbox._zindexch[i].o.o])
+551if(gbox._objects[gbox._zindexch[i].o.g][gbox._zindexch[i].o.o].__zt==null)
+552gbox._objects[gbox._zindexch[i].o.g][gbox._zindexch[i].o.o].__zt=gbox._zindex.addObject(gbox._zindexch[i].o,gbox._zindexch[i].z);
+553else
+554gbox._zindex.setPrio(gbox._objects[gbox._zindexch[i].o.g][gbox._zindexch[i].o.o].__zt,gbox._zindexch[i].z);
+555}
+556gbox._zindexch=[];
+557}
+558
+559
+560// Handle holding
+561for(varkeyingbox._keymap)
+562if(gbox._keyboard[gbox._keymap[key]]==-1)gbox._keyboard[gbox._keymap[key]]=0;else
+563if(gbox._keyboard[gbox._keymap[key]]&&(gbox._keyboard[gbox._keymap[key]]<100))gbox._keyboard[gbox._keymap[key]]++;
+564if(gbox._forcedidle)
+565this._gametimer=setTimeout(gbox._nextframe,gbox._forcedidle);// Wait for the browser
+566else
+567gbox._nextframe();
+568}
+569},
+570
+571/**
+572 * Displays basic audio, object, and performance statistics in the status bar. Automatically called each frame if the status bar is enabled.
+573 */
+574debugGetstats:function(){
+575varstatline="Idle: "+gbox._framestart+"/"+gbox._mspf+(gbox._frameskip>0?" ("+gbox._frameskip+"skip)":"")+" | ";
+576varcnt=0;
+577for(varg=0;g<gbox._groups.length;g++)
+578if(gbox._groupplay[gbox._groups[g]]){
+579cnt=0;
+580for(varobjingbox._objects[gbox._groups[g]])cnt++;
+581if(cnt)statline+=gbox._groups[g]+"["+cnt+"] ";
+582}
+583varcnt=0;
+584varply=0;
+585for(vargingbox._audio.aud)
+586for(varx=0;x<gbox._audio.aud[g].length;x++){
+587cnt++;
+588if(!gbox._audio.aud[g][x].paused&&!gbox._audio.aud[g][x].ended)ply++;
+589}
+590statline+="| audio: "+ply+"/"+cnt+":"+this._audioteam;
+591/*
+592 statline+="<br><br>";
+593 var id=gbox._zindex.first;
+594 while (id!=null) {
+595 if (gbox._groupplay[gbox._zindex.data[id].g]) statline+=gbox._zindex.data[id].g+" | "+gbox._zindex.data[id].o+" ("+gbox._zindex.data[id].__prio+")<br>";
+596 id=gbox._zindex.data[id].__next;
+597 }
+598 */
+599gbox.setStatBar(statline);
+600},
+601
+602setZindex:function(th,z){
+603if((th.__zt==null)||(th.zindex!=z)){
+604th.zindex=z;
+605this._zindexch.push({o:{g:th.group,o:th.id},z:z});
+606}
+607},
+608
+609/**
+610 * Returns true if a given key in this._keymap is pressed. Only returns true on the transition from unpressed to pressed.
+611 * @param {String} id A key in the keymap. By default, one of: "up" "down" "left" "right" "a" "b" "c"
+612 * @returns {Boolean} True if the given key is transitioning from unpressed to pressed in this frame.
+613 */
+614keyIsHit:function(id){returnthis._keyboard[this._keymap[id]]==1},
+615
+616/**
+617 * Returns true if a given key in this._keymap is being held down. Returns true as long as the key is held down.
+618 * @param {String} id A key in the keymap. By default, one of: "up" "down" "left" "right" "a" "b" "c"
+619 * @returns {Boolean} True if the given key is held down.
+620 */
+621keyIsPressed:function(id){returnthis._keyboard[this._keymap[id]]>0},
+622
+623/**
+624 * Returns true if a given key in this._keymap has been held down for at least one frame. Will not return true if a key
+625 * is quickly tapped, only once it has been held down for a frame.
+626 * @param {String} id A key in the keymap. By default, one of: "up" "down" "left" "right" "a" "b" "c"
+627 * @returns {Boolean} True if the given key has been held down for at least one frame.
+628 */
+629keyIsHold:function(id){returnthis._keyboard[this._keymap[id]]>1},
+630
+631/**
+632 * Returns true if a given key in this._keymap is released. Only returns true on the transition from pressed to unpressed.
+633 * @param {String} id A key in the keymap. By default, one of: "up" "down" "left" "right" "a" "b" "c"
+634 * @returns {Boolean} True if the given key is transitioning from pressed to unpressed in this frame.
+635 */
+636keyIsReleased:function(id){returnthis._keyboard[this._keymap[id]]==-1},
+637
+638_savesettings:function(){
+639varsaved="";
+640for(varkinthis._keymap)saved+="keymap-"+k+":"+this._keymap[k]+"~";
+641for(varfinthis._flags)saved+="flag-"+f+":"+(this._flags[f]?1:0)+"~";
+642this.dataSave("sys",saved);
+643},
+644_loadsettings:function(){
+645varcfg=this.dataLoad("sys");
+646if(cfg!==null){
+647cfg=cfg.split("~");
+648varkv;
+649varmk;
+650for(vari=0;i<cfg.length;i++){
+651kv=cfg[i].split(":");
+652mk=kv[0].split("-");
+653switch(mk[0]){
+654case"keymap":{this._keymap[mk[1]]=kv[1]*1;break}
+655case"flag":{this._flags[mk[1]]=kv[1]*1;break}
+656}
+657}
+658}
+659},
+660
+661/**
+662 * Saves data to a browser cookie as a key-value pair, which can be restored later using gbox.dataLoad. Only
+663 * works if user has cookies enabled.
+664 * @param {String} k The key which identifies the value you are storing.
+665 * @param {String} v The value you wish to store. Needs to be a string!
+666 * @param {String} d A date offset, to be added to the current date. Defines the cookie's expiration date. By default this is set to 10 years.
+667 * @example
+668 * // (from Capman)
+669 * gbox.dataSave("capman-hiscore",maingame.hud.getNumberValue("score","value"));
+670 */
+671dataSave:function(k,v,d){
+672vardate=newDate();
+673date.setTime(date.getTime()+((d?d:365*10)*24*60*60*1000));
+674document.cookie=this._systemcookie+"~"+k+"="+v+"; expires="+date.toGMTString()+"; path=/";
+675},
+676
+677/**
+678 * Loads data from a browser cookie. Send it a key and it returns a value (if available). Only works if user has cookies enabled.
+679 * @param {String} k The key which identifies the value you are loading.
+680 * @param {String} a A switch to determine whether a string or a number is returned. By default a string is returned.
+681 * @returns {Object} A string or a number loaded from the cookie.
+682 * @example
+683 * hiscore = gbox.dataLoad("hiscore");
+684 */
+685dataLoad:function(k,a){
+686varnameeq=this._systemcookie+"~"+k+"=";
+687varca=document.cookie.split(";");
+688varrt;
+689for(vari=0;i<ca.length;i++){
+690varc=ca[i];
+691while(c.charAt(0)==' ')c=c.substring(1,c.length);
+692if(c.indexOf(nameeq)==0){
+693rt=c.substring(nameeq.length,c.length);
+694if(a&&a.number)returnrt*1;elsereturnrt;
+695if(a&&a.number)returnrt*1;elsereturnrt;
+696}
+697}
+698returnnull;
+699},
+700
+701/**
+702 * Clears a value stored in a key-value pair in a browser cookie. Sets value to "". Only works if user has cookies enabled.
+703 * @param {String} k The key which identifies the value you are clearing.
+704 */
+705dataClear:function(k){this.dataSave(k,"",-1)},
+706
+707/**
+708 * Gets the current camera object.
+709 * @returns {Object} The camera object.
+710 */
+711getCamera:function(){returnthis._camera;},
+712
+713/**
+714 * Sets the y value of the current camera object.
+715 * @param {Integer} y The camera object's new y value.
+716 * @param {Object} viewdata An object containing parameters h and w, which are a bounding box that the camera is
+717 * not supposed to leave. For example, to use your map as a bounding area for the camera, pass along {w: map.w, h: map.h}.
+718 */
+719setCameraY:function(y,viewdata){
+720this._camera.y=y;
+721if(this._camera.y+this._camera.h>viewdata.h)this._camera.y=viewdata.h-this._screenh;
+722if(this._camera.y<0)this._camera.y=0;
+723},
+724
+725/**
+726 * Sets the x value of the current camera object.
+727 * @param {Integer} x The camera object's new x value.
+728 * @param {Object} viewdata An object containing parameters h and w, which are a bounding box that the camera is
+729 * not supposed to leave. For example, to use your map as a bounding area for the camera, pass along {w: map.w, h: map.h}.
+730 */
+731setCameraX:function(x,viewdata){
+732this._camera.x=x;
+733if(this._camera.x+this._camera.w>viewdata.w)this._camera.x=viewdata.w-this._screenw;
+734if(this._camera.x<0)this._camera.x=0;
+735},
+736
+737/**
+738 * Centers the camera.
+739 * @param {Object} data An object containing x and y parameters -- typically the object you wish to center the camera on.
+740 * @param {Object} viewdata An object containing parameters h and w, which are a bounding box that the camera is
+741 * not supposed to leave. For example, to use your map as a bounding area for the camera, pass along {w: map.w, h: map.h}.
+742 * @example
+743 * // Center the camera on the player object
+744 * gbox.centerCamera(gbox.getObject('player', 'player_id'), {w: map.w, h: map.h});
+745 */
+746centerCamera:function(data,viewdata){
+747this.setCameraX(data.x-this._screenhw,viewdata);
+748this.setCameraY(data.y-this._screenhh,viewdata);
+749},
+750
+751/**
+752 * Get an array containing the names of each group in the game, in order of rendering.
+753 * @returns {Array} An array of group names.
+754 * @example
+755 * grouplist = gbox.getGroups();
+756 * grouplist; // => ["background", "player", "enemy", "game"]
+757 */
+758getGroups:function(){returnthis._groups;},
+759
+760/**
+761 * Defines the names of each group in the game along with their rendering order.
+762 * @param {Array} g An array of strings of group names, in the order in which the groups should be rendered. So
+763 * g[0] will contain the first group to render, g[1] the second group to render, etc.
+764 */
+765setGroups:function(g){
+766this._groups=g;
+767this._groupplay[gbox.ZINDEX_LAYER]=true;
+768for(vari=0;i<g.length;i++)
+769if(!this._objects[g[i]]){
+770this._objects[g[i]]={};
+771this._groupplay[g[i]]=true;
+772this._renderorder[i]=g[i];
+773}
+774},
+775
+776/**
+777 * A method of setting the render order of groups independently of gbox.setGroups. Sets gbox._renderorder,
+778 * which by default is equivalent to gbox._groups. However, gbox._renderorder is what ultimately determines
+779 * the rendering order of groups. If you need to change your rendering order on the fly, use this function
+780 * by passing it a reordered array of group names.
+781 * @param {Array} g An array of strings of group names, in the order in which the groups should be rendered. So
+782 * g[0] will contain the first group to render, g[1] the second group to render, etc.
+783 */
+784setRenderOrder:function(g){this._renderorder=g;},
+785
+786/**
+787 * If a group is disabled, this will enable the group.
+788 * @param {String} gid The id of the group.
+789 */
+790playGroup:function(gid){this._groupplay[gid]=true;},
+791
+792/**
+793 * If a group is enabled, this will disable the group.
+794 * @param {String} gid The id of the group.
+795 */
+796stopGroup:function(gid){this._groupplay[gid]=false;},
+797
+798/**
+799 * Toggles a group between enabled and disabled status.
+800 * @param {String} gid The id of the group.
+801 */
+802toggleGroup:function(gid){this._groupplay[gid]=!this._groupplay[gid];},
+803
+804/**
+805 * Turns off all groups except for the one specified.
+806 * @param {String} gid The id of the group.
+807 */
+808soloGroup:function(gid){
+809for(vari=0;i<this._groups.length;i++)
+810if(this._groups[i]==gid)this.playGroup(this._groups[i]);elsethis.stopGroup(this._groups[i]);
+811},
+812
+813/**
+814 * Enables all groups, toggling any groups that are currently disabled.
+815 */
+816playAllGroups:function(){for(vari=0;i<this._groups.length;i++)this.playGroup(this._groups[i]);},
+817
+818/**
+819 * Destroys all objects in a given group.
+820 * @param {String} gid The id of the group.
+821 */
+822clearGroup:function(group){
+823for(varobjinthis._objects[group]){
+824if(this._objects[group][obj].__zt!=null)this._zindex.remove(this._objects[group][obj].__zt);
+825deletethis._objects[group][obj];
+826}
+827},
+828playGroups:function(gid){for(vari=0;i<gid.length;i++)this.playGroup(gid[i])},
+829stopGroups:function(gid){for(vari=0;i<gid.length;i++)this.stopGroup(gid[i])},
+830toggleGroups:function(gid){for(vari=0;i<gid.length;i++)this.toggleGroup(gid[i])},
+831
+832/**
+833 * Given a group and an id for a particular object instance, this returns the instance requested.
+834 * <b>NOTE:</b> this does not return a copy of the object you've requested! Any modifications you make
+835 * to the object returned are directly modifying the object you requested.
+836 * @param {String} group The id of the group that contains the object.
+837 * @param {String} id The id of the instance of the object.
+838 * @returns {Object} The object requested.
+839 * @example
+840 * // Find the player and reduce health by half.
+841 * playertemp = gbox.getObject('player','player_id');
+842 * player.health = player.health/2;
+843 */
+844getObject:function(group,id){returnthis._objects[group][id]},
+845
+846/**
+847 * Creates a font.
+848 * @param {Object} data An object containing: <ul><li>id: the id of the font</li>
+849 * <li>image: reference to the font image loaded (must contain font character tiles in ASCII order)</li>
+850 * <li>firstletter: the ASCII character that the font image's first character corresponds to</li>
+851 * <li>tileh: height in pixels of the character tiles</li>
+852 * <li>tilew: width in pixels of the character tiles</li>
+853 * <li>tilerow: width in pixels of each row in the font image</li>
+854 * <li>gapx: x-coord gap between tile columns, in pixels</li>
+855 * <li>gapy: y-coord gap between tile rows, in pixels</li></ul>
+856 * @example
+857 * gbox.addImage('font', 'font.png');
+858 * gbox.addFont({ id: 'small', image: 'font', firstletter: ' ', tileh: 8, tilew: 8, tilerow: 255, gapx: 0, gapy: 0 });
+859 */
+860addFont:function(data){
+861data.tilehh=Math.floor(data.tileh/2);
+862data.tilehw=Math.floor(data.tilew/2);
+863this._fonts[data.id]=data;
+864this._fonts[data.id].firstascii=data.firstletter.charCodeAt(0);
+865},
+866
+867/**
+868 * Returns a font object containing data about the font.
+869 * @param {String} id The id of the font, as set in gbox.addFont.
+870 */
+871getFont:function(id){
+872returnthis._fonts[id];
+873},
+874
+875/**
+876 * Deletes an object, keeping a record of its group and id in gbox._garbage.
+877 * @param {Object} obj The object you wish to delete.
+878 */
+879trashObject:function(obj){
+880if(!this._garbage[obj.group])this._garbage[obj.group]={};
+881this._garbage[obj.group][obj.id]=1;
+882obj.__trashing=true;
+883},
+884
+885/**
+886 * Clears the record held in gbox._garbage of what has been deleted.
+887 * @param {Object} obj The object you wish to delete.
+888 */
+889purgeGarbage:function(){
+890for(vargroupinthis._garbage)
+891for(varidinthis._garbage[group]){
+892if(this._objects[group][id].__zt!=null)
+893this._zindex.remove(this._objects[group][id].__zt)
+894deletethis._objects[group][id];
+895}
+896gbox._garbage={};
+897},
+898
+899/**
+900 * Deletes every object in a given group.
+901 * @param {String} group The group id.
+902 */
+903trashGroup:function(group){
+904if(!this._garbage[group])this._garbage[group]={};
+905for(varobjinthis._objects[group])
+906this._garbage[group][obj]=1;
+907},
+908
+909/**
+910 * Returns whether an object is due to be trashed. Useful in cases you want to check if
+911 * an object is marked as trash before it is actually deleted.
+912 * @param {Object} o The object you're checking.
+913 * @returns {Boolean} True if the object is marked as trash.
+914 */
+915objectIsTrash:function(o){returno.__trashing},
+916
+917/**
+918 * Creates a new game object. Generally speaking you pass a fully-defined object as the parameter (including a group, id, tileset, and so on).
+919 * A group must be specified, or the program will crash. If no id is specified, then it will automatically provide
+920 * an id of 'obj-XXXX' where 'XXXX' is an automatically incrementing integer. This is where the <i>initialize</i>, <i>first</i>, and <i>blit</i>
+921 * functions are defined, as well.
+922 * @param {Object} data The object you wish to create.
+923 * @returns {Object} The object you created.
+924 * @example
+925 * data = {
+926 * group: 'player',
+927 * id: 'player_id',
+928 * tileset: 'player_tiles',
+929 * x: 0,
+930 * y: 0,
+931 * initialize: function() {
+932 this.x = 10;
+933 this.y = 10;
+934 },
+935 * };
+936 * gbox.addObject(data);
+937 */
+938addObject:function(data){
+939// Extras
+940if(!data.id){
+941data.id="obj-"+this._autoid;
+942this._autoid=(this._autoid+1)%1000;
+943}
+944if(data.tileset){
+945if(data.h==null)data.h=this._tiles[data.tileset].tileh;
+946if(data.w==null)data.w=this._tiles[data.tileset].tilew;
+947if(data.hw==null)data.hw=this._tiles[data.tileset].tilehw;
+948if(data.hh==null)data.hh=this._tiles[data.tileset].tilehh;
+949}
+950this._objects[data.group][data.id]=data;
+951if(data.zindex!=null)
+952this.setZindex(this._objects[data.group][data.id],data.zindex);
+953returnthis._objects[data.group][data.id];
+954},
+955
+956/**
+957 * Returns whether a given group contains no objets.
+958 * @param {String} gid The group you're checking.
+959 * @returns {Boolean} True if the group contains no objects.
+960 */
+961groupIsEmpty:function(gid){for(variinthis._objects[gid])returnfalse;returntrue;},
+962
+963/**
+964 * Creates a new canvas. By default, the width and height is the current gbox._screenw and gbox._screenh,
+965 * but it can also be set by passing in a data object with the appropriate parameters.
+966 * @param {String} id The id of the new canvas.
+967 * @param {Object} data (Optional) The height and width of the new canvas, contained in data.h and data.w parameters.
+968 * @example
+969 * gbox.createCanvas('newCanvas', {w: 640, h: 480});
+970 */
+971createCanvas:function(id,data){
+972this.deleteCanvas(id);
+973this._canvas[id]=document.createElement("canvas");
+974this._canvas[id].setAttribute('height',(data&&data.h?data.h:this._screenh));
+975this._canvas[id].setAttribute('width',(data&&data.w?data.w:this._screenw));
+976},
+977
+978/**
+979 * Deletes a given canvas.
+980 * @param {String} id The id of the canvas to be deleted.
+981 */
+982deleteCanvas:function(id){
+983if(this._canvas[id])deletethis._canvas[id];
+984},
+985
+986/**
+987 * Checks to see if an image was successfully loaded.
+988 * @param {String} id The id of the image.
+989 * @returns {Boolean} True if the image has been loaded.
+990 */
+991imageIsLoaded:function(id){returnthis._images[id]&&(this._images[id].getAttribute("wasloaded"))&&this._images[id].width},
+992
+993/**
+994 * Gets information about a loaded image.
+995 * @param {String} id The id of the image.
+996 * @returns {Object} A DOM Image element, including the URL and last modified date of the image, its ID, and whether it was loaded successfully.
+997 * @example
+998 * image = gbox.getImage('logo');
+999 * image; // => <img src=?"logo.png?_brc=5-7-2010-15-48-42" src_org=?"logo.png" id=?"logo" wasloaded=?"true">?
+1000 */
+1001getImage:function(id){returnthis._images[id]},
+1002
+1003/**
+1004 * Gets the buffer canvas (automatically created by gbox.initScreen).
+1005 * @returns {Object} A DOM Canvas element, including the width and height of the canvas.
+1006 */
+1007getBuffer:function(){returnthis.getCanvas("_buffer")},
+1008
+1009/**
+1010 * Gets the buffer canvas context.
+1011 * @returns {Object} A DOM Canvas context object.
+1012 */
+1013getBufferContext:function(){return(gbox._fskid>=gbox._frameskip?(this._db?this.getCanvasContext("_buffer"):this._screen.getContext("2d")):null)},
+1014
+1015/**
+1016 * Gets a given canvas.
+1017 * @param {Object} id The identifier of the canvas.
+1018 * @returns {Object} A DOM Canvas element, including the width and height of the canvas.
+1019 */
+1020getCanvas:function(id){returnthis._canvas[id]},
+1021
+1022/**
+1023 * Gets the two-dimensional canvas context of a given canvas. The object it returns contains all the drawing functions for the canvas.
+1024 * See <a href = "http://dev.w3.org/html5/spec/Overview.html#the-canvas-element">W3C</a> and
+1025 * <a href = "https://developer.mozilla.org/en/canvas_tutorial/basic_usage">Mozilla Developer Center</a> for details.
+1026 * @param {Object} id The identifier of the canvas.
+1027 * @returns {Object} A DOM Canvas context object.
+1028 */
+1029getCanvasContext:function(id){returnthis.getCanvas(id).getContext("2d");},
+1030
+1031/**
+1032 * Adds an image file to the loader, assigning it to an ID. If adding an image to an existing ID, it checks to see if the file you're
+1033 * adding is different than the one currently assigned to the ID. If it's different, it overwrites the old image. If it's the same, then
+1034 * no action is taken.
+1035 * @param {String} id The identifier of the image.
+1036 * @param {String} filename The file name of the image.
+1037 */
+1038addImage:function(id,filename){
+1039if(this._images[id])
+1040if(this._images[id].getAttribute("src_org")==filename)
+1041return;
+1042else
+1043deletethis._images[id];
+1044this._addtoloader({type:"image",id:id,filename:filename});
+1045},
+1046
+1047/**
+1048 * Deletes an image currently in use. Does not delete the image file, but removes it from Akihabara's image list.
+1049 * @param {String} id The identifier of the image.
+1050 */
+1051deleteImage:function(id){
+1052deletethis._images[id];
+1053},
+1054
+1055/**
+1056 * Creates a new Akihabara tileset, adding it to the engine.
+1057 * @param {Object} t An object containing: <ul><li>id {String}: the new id of the tileset</li>
+1058 * <li>image {String}: reference to the tileset image loaded</li>
+1059 * <li>tileh {Integer}: height in pixels of the tiles</li>
+1060 * <li>tilew {Integer}: width in pixels of the tiles</li>
+1061 * <li>tilerow {Integer}: width in pixels of each row in the font image</li>
+1062 * <li>gapx {Integer}: x-coord gap between tile columns, in pixels</li>
+1063 * <li>gapy {Integer}: y-coord gap between tile rows, in pixels</li></ul>
+1064 */
+1065addTiles:function(t){
+1066t.tilehh=Math.floor(t.tileh/2);
+1067t.tilehw=Math.floor(t.tilew/2);
+1068this._tiles[t.id]=t;
+1069},
+1070
+1071/**
+1072 * Gets an Akihabara tileset, adding it to the engine.
+1073 * @param {String} t The ID of a tileset.
+1074 * @returns An object containing: <ul><li>id {String}: the new id of the tileset</li>
+1075 * <li>image {String}: reference to the tileset image loaded</li>
+1076 * <li>tileh {Integer}: height in pixels of the tiles</li>
+1077 * <li>tilew {Integer}: width in pixels of the tiles</li>
+1078 * <li>tilerow {Integer}: width in pixels of each row in the font image</li>
+1079 * <li>gapx {Integer}: x-coord gap between tile columns, in pixels</li>
+1080 * <li>gapy {Integer}: y-coord gap between tile rows, in pixels</li></ul>
+1081 */
+1082getTiles:function(t){returnthis._tiles[t]},
+1083
+1084/**
+1085 * Loads the initial splash screen and debugging font, then calls gbox._waitforloaded which adds to the game all the previously
+1086 * defined resources. Once gbox._waitforloaded is done, it calls the callback function cb.
+1087 * @params {String} cb The name of the function to be called when all assets are done loading.
+1088 */
+1089loadAll:function(cb){
+1090// Setup logger
+1091if(this._canlog)this.log=console.log;
+1092// Set the callback function, which is called after the resources are loaded.
+1093if(!this._cb)this._cb=cb;
+1094// Default stuff
+1095this.addImage("_dbf","akihabara/debugfont.png");
+1096if(this._splash.background)this.addImage("_splash",this._splash.background);
+1097gbox.addFont({id:"_dbf",image:"_dbf",firstletter:" ",tileh:5,tilew:4,tilerow:16,gapx:0,gapy:0});
+1098if(!gbox._splash.minimalTime)
+1099gbox._minimalexpired=2;
+1100this._waitforloaded();
+1101},
+1102
+1103_implicitsargs:function(data){
+1104if(data.camera){
+1105data.dx-=this._camera.x;
+1106data.dy-=this._camera.y;
+1107}
+1108if(data.sourcecamera){
+1109data.x=this._camera.x*(data.parallaxx?data.parallaxx:1);
+1110data.y=this._camera.y*(data.parallaxy?data.parallaxy:1);
+1111}
+1112},
+1113
+1114/**
+1115 * Draws a tile to a canvas context
+1116 * @param {Object} tox The canvas context to be drawn on.
+1117 * @param {Object} data An object containing data about the tile to be drawn, including:
+1118 * <ul><li>tileset {String}: the id of the tileset</li>
+1119 * <li>tile {Integer}: the index of the tile within the tileset to be drawn</li>
+1120 * <li>dx {Integer}: x coordinate to draw the tile at</li>
+1121 * <li>dy {Integer}: y coordinate to draw the tile at</li>
+1122 * <li>fliph {Integer}: horizontal flip, either 1 or -1</li>
+1123 * <li>flipv {Integer}: vertical flip, either 1 or -1</li>
+1124 * <li>alpha {Float}: alpha value (0 is transparent, 1 is opaque)</li></ul>
+1125 * @example
+1126 * // from capman, draws an current object's tile, called from inside its blit function
+1127 * gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:this.frame,dx:this.x,dy:this.y,fliph:this.fliph,flipv:this.flipv,camera:this.camera,alpha:1});
+1128 */
+1129blitTile:function(tox,data){
+1130if(tox==null)return;
+1131varts=this._tiles[data.tileset];
+1132varimg=this.getImage(ts.image);
+1133this._implicitsargs(data);
+1134tox.save();
+1135tox.globalAlpha=(data.alpha?data.alpha:1);
+1136tox.translate((data.fliph?ts.tilew:0),(data.flipv?ts.tileh:0));tox.scale((data.fliph?-1:1),(data.flipv?-1:1));
+1137this._safedrawimage(tox,img,ts.gapx+(ts.tilew*(data.tile%ts.tilerow)),ts.gapy+(ts.tileh*Math.floor(data.tile/ts.tilerow)),(data.w==null?ts.tilew:data.w),(data.h==null?ts.tileh:data.h),data.dx*(data.fliph?-1:1),data.dy*(data.flipv?-1:1),(data.w?data.w:ts.tilew),(data.h?data.h:ts.tileh));
+1138tox.restore();
+1139},
+1140
+1141/**
+1142 * Draws an image to a canvas context
+1143 * @param {Object} tox The canvas context to be drawn on.
+1144 * @param {Object} image The image to draw. Must be a DOM Image element, typicallly accessed via gbox.getImage
+1145 * @param {Object} data An object containing data about the tile to be drawn, including:
+1146 * <ul><li>dx {Integer}: (required) x coordinate to draw the image at</li>
+1147 * <li>dy {Integer}: (required) y coordinate to draw the image at</li>
+1148 * <li>fliph {Integer}: horizontal flip, either 1 or -1</li>
+1149 * <li>flipv {Integer}: vertical flip, either 1 or -1</li>
+1150 * <li>alpha {Float}: alpha value (0 is transparent, 1 is opaque)</li></ul>
+1151 * @example
+1152 * // draw an image at (100,100)
+1153 * gbox.blitAll(gbox.getBufferContext(),gbox.getImage("image_id"),{dx:100,dy:100});
+1154 */
+1155blitAll:function(tox,image,data){
+1156if(tox==null)return;
+1157this._implicitsargs(data);
+1158tox.save();
+1159tox.globalAlpha=(data.alpha?data.alpha:1);
+1160tox.translate((data.fliph?image.width:0),(data.flipv?image.height:0));tox.scale((data.fliph?-1:1),(data.flipv?-1:1));
+1161this._safedrawimage(tox,image,0,0,image.width,image.height,data.dx*(data.fliph?-1:1),data.dy*(data.flipv?-1:1),image.width,image.height);
+1162tox.restore();
+1163},
+1164
+1165blit:function(tox,image,data){
+1166if(tox==null)return;
+1167this._implicitsargs(data);
+1168tox.save();
+1169tox.globalAlpha=(data.alpha?data.alpha:1);
+1170tox.translate((data.fliph?data.dw:0),(data.flipv?data.dh:0));tox.scale((data.fliph?-1:1),(data.flipv?-1:1));
+1171this._safedrawimage(tox,image,(data.x?data.x:0),(data.y?data.y:0),(data.w?data.w:data.dw),(data.h?data.h:data.dh),data.dx*(data.fliph?-1:1),data.dy*(data.flipv?-1:1),data.dw,data.dh);
+1172tox.restore();
+1173},
+1174
+1175
+1176/**
+1177 * Draws a tilemap to a canvas context
+1178 * @param {Object} tox The canvas context to be drawn on.
+1179 * @param {Object} data An object containing a set of tilemap data, including:
+1180 * <ul><li>tileset {String}: (required) the id of the tileset the tilemap is based on</li>
+1181 * <li>map {Array}: an array whose x and y coord represent the tilemap coordinates, containing integers that correspond to the index of a given tile (or null for no tile)</li></ul>
+1182 */
+1183blitTilemap:function(tox,data){
+1184if(tox==null)return;
+1185varts=this._tiles[data.tileset];
+1186for(vary=0;y<data.map.length;y++)
+1187for(varx=0;x<data.map[y].length;x++)
+1188if(data.map[y][x]!=null)this.blitTile(tox,{tileset:data.tileset,tile:data.map[y][x],dx:x*ts.tilew,dy:y*ts.tilew});
+1189},
+1190
+1191
+1192/**
+1193 * Draws text to a canvas context
+1194 * @param {Object} tox The canvas context to be drawn on.
+1195 * @param {Object} data An object containing a set of data, including:
+1196 * <ul><li>font {String}: (required) the id of font to draw the text with</li>
+1197 * <li>text {String}: (required) the text to display</li>
+1198 * <li>dx {Integer}: (required) the x coordinate to draw the text at</li>
+1199 * <li>dy {Integer}: (required) the y coordinate to draw the text at</li>
+1200 * <li>dw {Integer}: the width of the text area -- required if you define data.halign</li>
+1201 * <li>dh {Integer}: the height of the text area -- required if you define data.valign</li>
+1202 * <li>valign {Integer}: either gbox.ALIGN_BOTTOM (aligns from the bottom of the text area) or gbox.ALIGN_MIDDLE (vertically centers text in text area)</li>
+1203 * <li>halign {Integer}: either gbox.ALIGN_RIGHT (aligns to the right hand side of text area) or gbox.ALIGN_CENTER (horizontallly centers text in text area)</li>
+1204 * <li>alpha {Float}: alpha value (0 is transparent, 1 is opaque)</li></ul>
+1205 */
+1206blitText:function(tox,data){
+1207if(tox==null)return;
+1208data.text+="";// Convert to string.
+1209varfn=this._fonts[data.font];
+1210vartile=0;
+1211this._implicitsargs(data);
+1212vardx=data.dx;
+1213vardy=data.dy;
+1214if(data.valign==gbox.ALIGN_BOTTOM)dy=dy+data.dh-fn.tileh;
+1215elseif(data.valign==gbox.ALIGN_MIDDLE)dy=dy+Math.floor(data.dh/2)-fn.tileh;
+1216if(data.halign==gbox.ALIGN_RIGHT)dx=dx+data.dw-(data.text.length*fn.tilew);
+1217elseif(data.halign==gbox.ALIGN_CENTER)dx=dx+Math.floor((data.dw-(data.text.length*fn.tilew))/2);
+1218tox.save();
+1219tox.globalAlpha=(data.alpha?data.alpha:1);
+1220for(vary=0;y<data.text.length;y++){
+1221tile=data.text.charCodeAt(y)-fn.firstascii;
+1222if(tile>=0){
+1223if(data.clear)tox.clearRect(dx+(y*fn.tilew),dy,(data.w?data.w:fn.tilew),(data.h?data.h:fn.tileh));
+1224this._safedrawimage(tox,this.getImage(fn.image),fn.gapx+(fn.tilew*(tile%fn.tilerow)),
+1225fn.gapy+(fn.tileh*Math.floor(tile/fn.tilerow)),fn.tilew,fn.tileh,dx+(y*fn.tilew),dy,(data.w?data.w:fn.tilew),(data.h?data.h:fn.tileh));
+1226}
+1227}
+1228tox.restore();
+1229},
+1230
+1231/**
+1232 * Clears a rectangular area of a canvas context.
+1233 * @param {Object} image The canvas context to be drawn on.
+1234 * @param {Object} data An object containing a set of data, including:
+1235 * <ul><li>x {Integer}: (required) the x coordinate of the top-left corner of the rectangle</li>
+1236 * <li>y {Integer}: (required) the y coordinate of the top-left corner of the rectangle</li>
+1237 * <li>w {Integer}: the width of the box; defaults to canvas width</li>
+1238 * <li>h {Integer}: the height the box; defaults to canvas height</li></ul>
+1239 */
+1240blitClear:function(image,data){
+1241if(image==null)return;
+1242if(data==null)data={x:0,y:0};
+1243this._implicitsargs(data);
+1244image.clearRect(data.x,data.y,(data.w==null?image.canvas.width:data.w),(data.h==null?image.canvas.height:data.h));
+1245},
+1246
+1247/**
+1248 * Draws an image directly to the screen's current canvas context. Used internally in gbox.go(). Probably shouldn't be used otherwise.
+1249 */
+1250blitImageToScreen:function(image){
+1251this._screen.getContext("2d").drawImage(image,0,0);
+1252},
+1253
+1254/**
+1255 * Draws a filled rectangle over an entire canvas context.
+1256 * @param {Object} tox The canvas context to be filled.
+1257 * @param {Object} data An object containing a set of data, including:
+1258 * <ul><li>alpha {Float}: the alpha value of the rectangle; defaults to 1</li>
+1259 * <li>color {Object}: the color of the box, formatted rgb(rValue, gValue, bValue); default black</li></ul>
+1260 */
+1261blitFade:function(tox,data){
+1262if(tox)this.blitRect(tox,{x:0,y:0,w:tox.canvas.width,h:tox.canvas.height,alpha:data.alpha,color:data.color});
+1263},
+1264
+1265/**
+1266 * Draws a filled rectangle to a canvas context.
+1267 * @param {Object} tox The canvas context to be drawn on.
+1268 * @param {Object} data An object containing a set of data, including:
+1269 * <ul><li>x {Integer}: (required) the x coordinate of the top-left corner of the rectangle</li>
+1270 * <li>y {Integer}: (required) the y coordinate of the top-left corner of the rectangle</li>
+1271 * <li>w {Integer}: (required) the width of the box</li>
+1272 * <li>h {Integer}: (required) the height the box</li>
+1273 * <li>alpha {Float}: the alpha value of the rectangle; defaults to 1</li>
+1274 * <li>color {Object}: the color of the box, formatted rgb(rValue, gValue, bValue); default black</li></ul>
+1275 */
+1276blitRect:function(tox,data){
+1277if(tox==null)return;
+1278tox.save();
+1279tox.globalAlpha=(data.alpha?data.alpha:1);
+1280tox.fillStyle=(data.color?data.color:gbox.COLOR_BLACK);
+1281tox.fillRect(data.x,data.y,data.w,data.h);
+1282tox.restore();
+1283},
+1284
+1285/**
+1286 * Calculates a box collision between two collision boxes within a given tolerance. A higher tolerance means less precise collision.
+1287 * @param {Object} o1 A collision box you're testing for collision. Must contain:
+1288 * <ul><li>x {Integer}: (required) the x coordinate of the object's origin; assumes the Akihabara default of top-left being the origin</li>
+1289 * <li>y {Integer}: (required) the y coordinate of the object's origin; assumes the Akihabara default of top-left being the origin</li>
+1290 * <li>w {Integer}: (required) the width of the box</li>
+1291 * <li>h {Integer}: (required) the height the box</li></ul>
+1292 * @param {Object} o2 A collision box you're testing for collision. Must contain:
+1293 * <ul><li>x {Integer}: (required) the x coordinate of the object's origin; assumes the Akihabara default of top-left being the origin</li>
+1294 * <li>y {Integer}: (required) the y coordinate of the object's origin; assumes the Akihabara default of top-left being the origin</li>
+1295 * <li>w {Integer}: (required) the width of the box</li>
+1296 * <li>h {Integer}: (required) the height the box</li></ul>
+1297 * @param {Integer} t The tolerance for the collision, in pixels. A value of 0 means pixel-perfect box collision. A value of 2 would mean that the
+1298 * boxes could overlap by up to 2 pixels without being considered a collision.
+1299 * @returns True if the two collision boxes are colliding within the given tolerance.
+1300 */
+1301collides:function(o1,o2,t){
+1302if(!t)t=0;
+1303return!((o1.y+o1.h-1-t<o2.y+t)||(o1.y+t>o2.y+o2.h-1-t)||(o1.x+o1.w-1-t<o2.x+t)||(o1.x+t>o2.x+o2.w-1-t));
+1304},
+1305
+1306/**
+1307 * Calculates a point-box collision between a point and a collision box within a given tolerance. A higher tolerance means less precise collision.
+1308 * @param {Object} o1 A point you're testing for collision. Must contain:
+1309 * <ul><li>x {Integer}: (required) the x coordinate of the point</li>
+1310 * <li>y {Integer}: (required) the y coordinate of the point</li></ul>
+1311 * @param {Object} o2 A collision box you're testing for collision. Must contain:
+1312 * <ul><li>x {Integer}: (required) the x coordinate of the object's origin; assumes the Akihabara default of top-left being the origin</li>
+1313 * <li>y {Integer}: (required) the y coordinate of the object's origin; assumes the Akihabara default of top-left being the origin</li>
+1314 * <li>w {Integer}: (required) the width of the box</li>
+1315 * <li>h {Integer}: (required) the height the box</li></ul>
+1316 * @param {Integer} t The tolerance for the collision, in pixels. A value of 0 means pixel-perfect collision. A value of 2 would mean that the
+1317 * point could exist within the outermost 2 pixels of the box without being considered a collision.
+1318 * @returns True if the point is colliding with the box within the given tolerance.
+1319 */
+1320pixelcollides:function(o1,o2,t){
+1321if(!t)t=0;
+1322return!((o1.y<o2.y+t)||(o1.y>o2.y+o2.h-1-t)||(o1.x<o2.x+t)||(o1.x>o2.x+o2.w-1-t));
+1323},
+1324
+1325/**
+1326 * Determines whether an object is visible by seeing if it collides with the camera's viewport.
+1327 * @param {Object} obj The object you're testing to see if it's visible. Must contain:
+1328 * <ul><li>x {Integer}: (required) the x coordinate of the object's origin; assumes the Akihabara default of top-left being the origin</li>
+1329 * <li>y {Integer}: (required) the y coordinate of the object's origin; assumes the Akihabara default of top-left being the origin</li>
+1330 * <li>w {Integer}: (required) the width of the object's collision box</li>
+1331 * <li>h {Integer}: (required) the height the object's box</li></ul>
+1332 * @returns True if the object's collision box is within the camera's viewport.
+1333 */
+1334objectIsVisible:function(obj){returnthis.collides(obj,this._camera,0);},
+1335
+1336// ---
+1337// ---
+1338// --- AUDIO ENGINE
+1339// ---
+1340// ---
+1341
+1342_audiochannels:{},
+1343_audiomastervolume:1.0,
+1344_canaudio:false,
+1345_audiodequeuetime:0,
+1346_audioprefetch:0.5,
+1347_audiocompatmode:0,// 0: pause/play, 1: google chrome compatibility, 2: ipad compatibility (single channel)
+1348_createmode:0,// 0: clone, 1: rehinstance
+1349_fakecheckprogressspeed:100,// Frequency of fake audio monitoring
+1350_fakestoptime:1,// Fake audio stop for compatibility mode
+1351_audioteam:2,
+1352_loweraudioteam:1,
+1353_audio:{lding:null,qtimer:false,aud:{},ast:{}},
+1354_audioactions:[],
+1355_showplayers:false,
+1356_singlechannelname:"bgmusic",
+1357_positiondelay:0,
+1358_playerforcer:0,
+1359_audiomutevolume:0.0001,// Zero is still not accepted by everyone :(
+1360_rawstopaudio:function(su){
+1361if(gbox._audiocompatmode==1){
+1362if(su.duration-su.currentTime>gbox._fakestoptime)
+1363su.currentTime=su.duration-gbox._fakestoptime;
+1364su.muted=true;
+1365}else
+1366su.pause();
+1367
+1368},
+1369_rawplayaudio:function(su){
+1370if(gbox._audiocompatmode==1){
+1371try{su.currentTime=0;}catch(e){}
+1372su.muted=false;
+1373su.play();
+1374}elseif(gbox._audiocompatmode==2){
+1375su.load();
+1376gbox._playerforcer=setInterval(function(e){try{su.play();clearInterval(gbox._playerforcer)}catch(e){}},1000);
+1377}else{
+1378try{su.currentTime=0;}catch(e){}
+1379su.play();
+1380}
+1381},
+1382_finalizeaudio:function(ob,who,donext){
+1383
+1384varcur=(who?who:this);
+1385cur.removeEventListener('ended',gbox._finalizeaudio,false);
+1386cur.removeEventListener('timeupdate',gbox._checkprogress,false);
+1387
+1388cur.addEventListener('ended',gbox._playbackended,false);
+1389if(donext)gbox._loaderloaded();
+1390},
+1391_audiodoload:function(){
+1392if(gbox._audiocompatmode==1)gbox._audio.lding.muted=true;
+1393elseif(gbox._audiocompatmode==2)
+1394gbox._finalizeaudio(null,gbox._audio.lding,true);
+1395else{
+1396gbox._audio.lding.load();
+1397gbox._audio.lding.play();
+1398}
+1399},
+1400_timedfinalize:function(){
+1401gbox._rawstopaudio(gbox._audio.lding);
+1402gbox._finalizeaudio(null,gbox._audio.lding,true);
+1403},
+1404_checkprogress:function(){
+1405if(gbox._audio.lding.currentTime>gbox._audioprefetch)gbox._timedfinalize();
+1406},
+1407_fakecheckprogress:function(){
+1408if(gbox._audio.lding.currentTime>gbox._audioprefetch)gbox._timedfinalize();elsesetTimeout(gbox._fakecheckprogress,gbox._fakecheckprogressspeed);
+1409},
+1410_audiofiletomime:function(f){
+1411varfsp=f.split(".");
+1412switch(fsp.pop().toLowerCase()){
+1413case"ogg":{return"audio/ogg";break}
+1414case"mp3":{return"audio/mpeg";break}
+1415default:{
+1416return"audio/mpeg";
+1417}
+1418}
+1419},
+1420_pushaudio:function(){try{this.currentTime=1.0}catch(e){}},
+1421_createnextaudio:function(cau){
+1422if(cau.def){
+1423gbox.deleteAudio(cau.id);
+1424this._audio.aud[cau.id]=[];
+1425this._audio.ast[cau.id]={cy:-1,volume:1,channel:null,play:false,mute:false,filename:cau.filename[0]};
+1426if(cau.def)for(varaincau.def)this._audio.ast[cau.id][a]=cau.def[a];
+1427}
+1428if((gbox._createmode==0)&&(cau.team>0)){
+1429varael=this._audio.aud[cau.id][0].cloneNode(true);
+1430gbox._finalizeaudio(null,ael,false);
+1431}else{
+1432varael=document.createElement('audio');
+1433ael.volume=gbox._audiomutevolume;
+1434}
+1435if(!gbox._showplayers){
+1436ael.style.display="none";
+1437ael.style.visibility="hidden";
+1438ael.style.width="1px";
+1439ael.style.height="1px";
+1440ael.style.position="absolute";
+1441ael.style.left="0px";
+1442ael.style.top="-1000px";
+1443}
+1444ael.setAttribute('controls',gbox._showplayers);
+1445ael.setAttribute('aki_id',cau.id);
+1446ael.setAttribute('aki_cnt',cau.team);
+1447ael.addEventListener('loadedmetadata',gbox._pushaudio,false);// Push locked audio in safari
+1448if(((gbox._createmode==0)&&(cau.team==0))||(gbox._createmode==1)){
+1449if(ael.canPlayType){
+1450varcmime;
+1451for(vari=0;i<cau.filename.length;i++){
+1452cmime=gbox._audiofiletomime(cau.filename[i]);
+1453if(("no"!=ael.canPlayType(cmime))&&(""!=ael.canPlayType(cmime))){
+1454ael.src=gbox._breakcacheurl(cau.filename[i]);
+1455break;
+1456}
+1457}
+1458}else{
+1459for(vari=0;i<cau.filename.length;i++){
+1460varsrc=document.createElement('source');
+1461src.setAttribute('src',gbox._breakcacheurl(cau.filename[i]));
+1462ael.appendChild(src);
+1463}
+1464}
+1465ael.addEventListener('ended',this._finalizeaudio,false);
+1466if(gbox._audiocompatmode==1)
+1467setTimeout(gbox._fakecheckprogress,gbox._fakecheckprogressspeed);
+1468else
+1469ael.addEventListener('timeupdate',this._checkprogress,false);
+1470ael.setAttribute('buffering',"auto");
+1471ael.volume=0;
+1472this._audio.aud[cau.id].push(ael);
+1473document.body.appendChild(ael);
+1474gbox._audio.lding=ael;
+1475setTimeout(gbox._audiodoload,1);
+1476}else{
+1477this._audio.aud[cau.id].push(ael);
+1478document.body.appendChild(ael);
+1479gbox._loaderloaded();
+1480}
+1481},
+1482_playbackended:function(e){
+1483if(gbox._audio.ast[this.getAttribute('aki_id')].cy==this.getAttribute('aki_cnt')){
+1484if(gbox._audio.ast[this.getAttribute('aki_id')].play&&gbox._audio.ast[this.getAttribute('aki_id')].loop)
+1485if(gbox._audiocompatmode==2)
+1486gbox._rawplayaudio(this);
+1487else
+1488this.currentTime=0;
+1489else
+1490gbox._audio.ast[this.getAttribute('aki_id')].play=false;
+1491}elseif(gbox._audiocompatmode==1){
+1492this.pause();
+1493this.muted=false;
+1494}
+1495},
+1496_updateaudio:function(a){
+1497if(this._audio.ast[a].play){
+1498this._audio.aud[a][this._audio.ast[a].cy].volume=(this._audio.ast[a].mute?this._audiomutevolume:
+1499this._audiomastervolume*
+1500(this._audio.ast[a].volume!=null?this._audio.ast[a].volume:1)*
+1501((this._audio.ast[a].channel!=null)&&(this._audiochannels[this._audio.ast[a].channel]!=null)&&(this._audiochannels[this._audio.ast[a].channel].volume!=null)?this._audiochannels[this._audio.ast[a].channel].volume:1)
+1502)
+1503}
+1504},
+1505_minimaltimeexpired:function(){gbox._minimalexpired=2;},
+1506_splashscreeniscompleted:function(){return(gbox._splash.background?gbox.imageIsLoaded("_splash"):true)&&(gbox._splash.minilogo?gbox.imageIsLoaded("logo"):true)&&(gbox._splash.footnotes?gbox.imageIsLoaded("_dbf"):true)},
+1507_addqueue:function(a){
+1508if(!gbox._audiodequeuetime)
+1509gbox._dequeueaudio(null,a);
+1510else{
+1511gbox._audioactions.push(a);
+1512if(!gbox._audio.qtimer){
+1513gbox._audio.qtimer=true;
+1514setTimeout(gbox._dequeueaudio,gbox._audiodequeuetime);
+1515}
+1516}
+1517},
+1518_dequeueaudio:function(k,rt){
+1519varac=(rt?rt:gbox._audioactions.pop());
+1520switch(ac.t){
+1521case0:{
+1522gbox._updateaudio(ac.a.getAttribute("aki_id"));
+1523gbox._rawplayaudio(ac.a);
+1524break
+1525}
+1526case1:{
+1527gbox._rawstopaudio(ac.a);
+1528break;
+1529}
+1530case2:{
+1531gbox._updateaudio(ac.a.getAttribute("aki_id"));
+1532break;
+1533}
+1534}
+1535if(!rt&&gbox._audioactions.length){
+1536gbox._audio.qtimer=true;
+1537setTimeout(gbox._dequeueaudio,gbox._audiodequeuetime);
+1538}elsegbox._audio.qtimer=false;
+1539
+1540},
+1541getAudioIsSingleChannel:function(){returnthis._audiocompatmode==2;},
+1542setAudioPositionDelay:function(m){gbox._positiondelay=m},
+1543setAudioDequeueTime:function(m){gbox._audiodequeuetime=m},
+1544setShowPlayers:function(m){gbox._showplayers=m},
+1545setAudioCompatMode:function(m){gbox._audiocompatmode=m},
+1546setAudioCreateMode:function(m){gbox._createmode=m},
+1547addAudio:function(id,filename,def){
+1548if(gbox._canaudio){
+1549if(gbox._audio.aud[id])
+1550if(gbox._audio.ast[id].filename==filename[0])
+1551return;
+1552else
+1553gbox.deleteAudio(id);
+1554if((gbox._audiocompatmode!=2)||(def.channel==gbox._singlechannelname)){
+1555vargrsize=(def.channel==gbox._singlechannelname?gbox._loweraudioteam:(def.background?gbox._loweraudioteam:gbox._audioteam));
+1556for(vari=0;i<grsize;i++)
+1557gbox._addtoloader({type:"audio",data:{id:id,filename:filename,def:(i==0?def:null),team:i}});
+1558}
+1559}
+1560},
+1561deleteAudio:function(id){
+1562if(gbox._audio.aud[id]){
+1563for(vari=0;i<gbox._audio.aud[id].length;i++){
+1564try{document.body.removeChild(gbox._audio.aud[id][i]);}catch(e){}
+1565deletegbox._audio.aud[id][i];
+1566}
+1567deletegbox._audio.aud[id];
+1568if(gbox._audio.ast[id])deletegbox._audio.ast[id];
+1569}
+1570},
+1571playAudio:function(a,data){
+1572if(this._canaudio&&this._audio.ast[a])
+1573if(!this._audio.ast[a].play)this.hitAudio(a,data);
+1574},
+1575hitAudio:function(a,data){
+1576if(this._canaudio&&this._audio.ast[a]){
+1577varael;
+1578if(this._audio.ast[a].cy!=-1)
+1579this.stopAudio(a,true);
+1580this._audio.ast[a].cy=(this._audio.ast[a].cy+1)%this._audio.aud[a].length;
+1581ael=this._audio.aud[a][this._audio.ast[a].cy];
+1582if(data)
+1583for(varnindata)this._audio.ast[a][n]=data[n];
+1584this._audio.ast[a].play=true;
+1585this._addqueue({t:0,a:ael});
+1586}
+1587},
+1588stopAudio:function(a,permissive){
+1589if(this._canaudio){
+1590varael;
+1591if(this._canaudio&&this._audio.ast[a]&&this._audio.ast[a].play){
+1592this._audio.ast[a].play=false;
+1593ael=this._audio.aud[a][this._audio.ast[a].cy];
+1594if(ael.duration-1.5>0)
+1595this._addqueue({t:1,a:ael});
+1596}
+1597}
+1598},
+1599setSplashSettings:function(a){for(varnina)this._splash[n]=a[n];},
+1600resetChannel:function(ch){
+1601if(this._canaudio&&this._audiochannels[ch])
+1602if(ch=="master")
+1603for(varchinthis._audiochannels)
+1604this.setChannelVolume(ch,this._audiochannels[ch]._def.volume);
+1605elseif(this._audiochannels[ch])
+1606this.setChannelVolume(ch,this._audiochannels[ch]._def.volume);
+1607},
+1608getChannelDefaultVolume:function(ch){
+1609if(this._canaudio&&this._audiochannels[ch])returnthis._audiochannels[ch]._def.volume;elsereturnnull;
+1610},
+1611setChannelVolume:function(ch,a){
+1612if(this._canaudio&&this._audiochannels[ch]){
+1613if(ch=="master")this._audiomastervolume=a;elsethis._audiochannels[ch].volume=a
+1614for(varjingbox._audio.aud)
+1615if(this._audio.ast[j].cy>-1)this._updateaudio(j);
+1616}
+1617},
+1618getChannelVolume:function(ch){if(ch=="master")returnthis._audiomastervolume;elseif(this._audiochannels[ch])returnthis._audiochannels[ch].volume;elsereturn0},
+1619changeChannelVolume:function(ch,a){
+1620if(this._canaudio&&this._audiochannels[ch]){
+1621varvol=this.getChannelVolume(ch)+a;
+1622if(vol>1)vol=1;elseif(vol<0)vol=0;
+1623this.setChannelVolume(ch,vol);
+1624}
+1625},
+1626stopChannel:function(ch){
+1627if(this._canaudio)
+1628for(varjingbox._audio.aud)
+1629if(this._audio.ast[j].cy>-1&&gbox._audio.ast[j].play&&((ch=="master")||(this._audio.ast[j].channel==ch)))
+1630this.stopAudio(j);
+1631},
+1632
+1633setAudioUnmute:function(a){if(this._canaudio&&this._audio.ast[a]){this._audio.ast[a].mute=false;this._updateaudio(a);}},
+1634setAudioMute:function(a){if(this._canaudio&&this._audio.ast[a]){this._audio.ast[a].mute=true;this._updateaudio(a);}},
+1635getAudioMute:function(a){if(this._canaudio&&this._audio.ast[a])returnthis._audio.ast[a].mute;elsereturnnull},
+1636
+1637setAudioVolume:function(a,vol){if(this._canaudio&&this._audio.ast[a]){this._audio.ast[a].volume=vol;this._updateaudio(a);}},
+1638getAudioVolume:function(a,vol){if(this._canaudio&&this._audio.ast[a])returnthis._audio.ast[a].volume;elsereturnnull},
+1639
+1640setAudioPosition:function(a,p){if(this._canaudio&&this._audio.ast[a]&&this._audio.aud[a][this._audio.ast[a].cy])this._audio.aud[a][this._audio.ast[a].cy].currentTime=p;},
+1641getAudioPosition:function(a){if(this._canaudio&&this._audio.ast[a]&&this._audio.aud[a][this._audio.ast[a].cy])if(this._audio.aud[a][this._audio.ast[a].cy].currentTime>this._positiondelay)returnthis._audio.aud[a][this._audio.ast[a].cy].currentTime-this._positiondelay;elsereturn0;elsereturn0},
+1642
+1643getAudioDuration:function(a){if(this._canaudio&&this._audio.ast[a]&&this._audio.aud[a][this._audio.ast[a].cy])returnthis._audio.aud[a][this._audio.ast[a].cy].duration;elsereturn0},
+1644
+1645changeAudioVolume:function(a,vol){if(this._canaudio&&this._audio.ast[a]){if(this._audio.ast[a].volume+vol>1)this._audio.ast[a].volume=1;elseif(this._audio.ast[a].volume+vol<0)this._audio.ast[a].volume=0;elsethis._audio.ast[a].volume+=vol;this._updateaudio(a);}},
+1646setCanAudio:function(a){this._canaudio=!this._flags.noaudio&&a;},
+1647setAudioChannels:function(a){
+1648this._audiochannels=a;
+1649for(varchina){
+1650this._audiochannels[ch]._def={};
+1651for(varattrinthis._audiochannels[ch])
+1652if(attr!="_def")this._audiochannels[ch]._def[attr]=this._audiochannels[ch][attr];
+1653}
+1654},
+1655setAudioTeam:function(a){this._audioteam=a;},
+1656setLowerAudioTeam:function(a){this._loweraudioteam=a;},
+1657
+1658// ---
+1659// ---
+1660// --- DYNAMIC SCRIPT INCLUSION
+1661// ---
+1662// ---
+1663
+1664addScript:function(call){
+1665gbox._addtoloader({type:"script",call:call});
+1666},
+1667
+1668// ---
+1669// ---
+1670// --- BUNDLES
+1671// ---
+1672// ---
+1673
+1674addBundle:function(call){
+1675gbox._addtoloader({type:"bundle",call:call});
+1676},
+1677
+1678readBundleData:function(pack,call){
+1679// Local resources first
+1680if(pack.setObject)for(vari=0;i<pack.setObject.length;i++)eval("("+pack.setObject[i].object+")")[pack.setObject[i].property]=pack.setObject[i].value;
+1681if(pack.addFont)for(vari=0;i<pack.addFont.length;i++)gbox.addFont(pack.addFont[i]);
+1682if(pack.addTiles)for(vari=0;i<pack.addTiles.length;i++)gbox.addTiles(pack.addTiles[i]);
+1683// Remote resources for last
+1684if(pack.addImage)for(vari=0;i<pack.addImage.length;i++)gbox.addImage(pack.addImage[i][0],pack.addImage[i][1]);
+1685if(pack.addAudio)for(vari=0;i<pack.addAudio.length;i++)gbox.addAudio(pack.addAudio[i][0],pack.addAudio[i][1],pack.addAudio[i][2]);
+1686if(pack.addBundle)for(vari=0;i<pack.addBundle.length;i++)gbox.addBundle(pack.addBundle[i]);
+1687if(pack.addScript)for(vari=0;i<pack.addScript.length;i++)gbox.addScript(pack.addScript[i]);
+1688// Trigger the onLoad events in resource and loader
+1689if(pack.onLoad)gbox._addtoloader({type:"exec-onl",func:pack.onLoad,call:call,pack:pack});
+1690if(call.onLoad)gbox._addtoloader({type:"exec-onl",func:call.onLoad,call:call,pack:pack});
+1691},
+1692
+1693// ---
+1694// ---
+1695// --- DATA LOADER
+1696// ---
+1697// ---
+1698
+1699_xmlhttp:null,
+1700_loaderqueue:cyclelist.create(200),
+1701_loadercache:cachelist.create(30),
+1702
+1703// Callback for loaded image
+1704_loaderimageloaded:function(){
+1705this.setAttribute('wasloaded',true);
+1706this.hheight=Math.floor(this.height/2);
+1707this.hwidth=Math.floor(this.width/2);
+1708gbox._loaderloaded();
+1709},
+1710// Callback for loaded bundle
+1711_loaderhmlhttploading:function(){
+1712if(this.readyState==4&&(this.status==0||this.status==200)){
+1713if(this.responseText){
+1714if(!gbox._loaderqueue.getCurrent().call.skipCacheSave)
+1715gbox._loadercache.add(gbox._loaderqueue.getCurrent().call.file,this.responseText);
+1716varpack=eval("("+this.responseText+")");
+1717gbox.readBundleData(pack,gbox._loaderqueue.getCurrent().call);
+1718// Keep loading the other resources.
+1719gbox._loaderloaded();
+1720}
+1721}
+1722},
+1723
+1724// Loader code
+1725_addtoloader:function(d){// type:xx, data:yy
+1726gbox._loaderqueue.push(d);
+1727if(!gbox._loaderqueue.isProcessing())
+1728gbox._loadnext();
+1729},
+1730_loaderloaded:function(){
+1731setTimeout(gbox._loadnext,10);
+1732},
+1733_loaderscript:function(){
+1734if(gbox._loaderqueue.getCurrent().call.onLoad)gbox._addtoloader({type:"exec-onl",func:gbox._loaderqueue.getCurrent().call.onLoad,call:gbox._loaderqueue.getCurrent().call});
+1735gbox._loadnext();
+1736},
+1737_loadnext:function(){
+1738varcurrent=gbox._loaderqueue.pop();
+1739if(gbox._loaderqueue.isProcessing()){
+1740switch(gbox._loaderqueue.getCurrent().type){
+1741case"image":{
+1742gbox._images[current.id]=newImage();
+1743gbox._images[current.id].addEventListener('load',gbox._loaderimageloaded,false);
+1744gbox._images[current.id].src=gbox._breakcacheurl(current.filename);
+1745gbox._images[current.id].setAttribute('src_org',current.filename);
+1746gbox._images[current.id].setAttribute('id',current.id);
+1747gbox._images[current.id].setAttribute('wasloaded',false);
+1748break;
+1749}
+1750case"bundle":{
+1751vardone=false;
+1752if(!current.call.skipCacheLoad){
+1753vardata=gbox._loadercache.read(current.call.file);
+1754if(data){
+1755varpack=eval("("+data+")");
+1756gbox.readBundleData(pack,current.call);
+1757// Keep loading the other resources.
+1758gbox._loaderloaded();
+1759done=true;
+1760}
+1761}
+1762if(!done){
+1763gbox._xmlhttp=newXMLHttpRequest();
+1764gbox._xmlhttp.open((current.call.data?"POST":"GET"),gbox._breakcacheurl(current.call.file),true);
+1765gbox._xmlhttp.onreadystatechange=gbox._loaderhmlhttploading;
+1766if(current.call.data){
+1767gbox._xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
+1768gbox._xmlhttp.send(current.call.data);
+1769}elsegbox._xmlhttp.send();
+1770}
+1771break;
+1772}
+1773case"audio":{
+1774gbox._createnextaudio(current.data);
+1775break;
+1776}
+1777case"exec-onl":{
+1778current.func(current.call,current.pack);
+1779gbox._loaderloaded();
+1780break;
+1781}
+1782case"script":{
+1783varscript=document.createElement('script');
+1784script.type="text/javascript";
+1785script.onload=gbox._loaderscript;
+1786script.src=current.call.file;
+1787document.getElementsByTagName('body')[0].appendChild(script);
+1788break;
+1789}
+1790}
+1791}
+1792
+1793},
+1794_waitforloaded:function(){
+1795varaul;
+1796if(gbox._loaderqueue.isBusy()||(gbox._minimalexpired!=2)){
+1797vartox=gbox._screen.getContext("2d");
+1798tox.save();
+1799gbox.blitFade(tox,{alpha:1});
+1800if(!gbox._minimalexpired&&gbox._splashscreeniscompleted()){
+1801gbox._minimalexpired=1;
+1802setTimeout(gbox._minimaltimeexpired,gbox._splash.minimalTime);
+1803}
+1804if(gbox._splash.loading)gbox._splash.loading(tox,gbox._loaderqueue.getDone(),gbox._loaderqueue.getTotal());
+1805if(gbox._splash.background&&gbox.imageIsLoaded("_splash"))
+1806gbox.blit(tox,gbox.getImage("_splash"),{w:gbox.getImage("_splash").width,h:gbox.getImage("_splash").height,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH()});
+1807if(gbox._splash.minilogo&&gbox.imageIsLoaded("logo")){
+1808vardw=gbox.getScreenW()/4;
+1809vardh=(gbox.getImage("logo").height*dw)/gbox.getImage("logo").width
+1810gbox.blit(tox,gbox.getImage(gbox._splash.minilogo),{w:gbox.getImage("logo").width,h:gbox.getImage("logo").height,dx:gbox.getScreenW()-dw-5,dy:gbox.getScreenH()-dh-5,dw:dw,dh:dh});
+1811}
+1812if(gbox._splash.footnotes&&gbox.imageIsLoaded("_dbf")){
+1813if(!gbox.getCanvas("_footnotes")){
+1814varfd=gbox.getFont("_dbf");
+1815gbox.createCanvas("_footnotes",{w:gbox.getScreenW()-5,h:(gbox._splash.footnotes.length)*(fd.tileh+gbox._splash.footnotesSpacing)});
+1816for(vari=0;i<gbox._splash.footnotes.length;i++)
+1817gbox.blitText(gbox.getCanvasContext("_footnotes"),{
+1818font:"_dbf",
+1819dx:0,
+1820dy:i*(fd.tileh+gbox._splash.footnotesSpacing),
+1821text:gbox._splash.footnotes[i]
+1822});
+1823}
+1824gbox.blitAll(tox,gbox.getCanvas("_footnotes"),{dx:5,dy:gbox.getScreenH()-gbox.getCanvas("_footnotes").height-5});
+1825}
+1826if(gbox._loaderqueue.isBusy()){
+1827varbw=Math.floor(((gbox.getScreenW()-4)*gbox._loaderqueue.getDone())/gbox._loaderqueue.getTotal());
+1828tox.globalAlpha=1;
+1829tox.fillStyle=gbox._splash.gaugeBorderColor;
+1830tox.fillRect(0,Math.floor((gbox.getScreenH()-gbox._splash.gaugeHeight)/2),gbox.getScreenW(),gbox._splash.gaugeHeight);
+1831tox.fillStyle=gbox._splash.gaugeBackColor;
+1832tox.fillRect(1,Math.floor(((gbox.getScreenH()-gbox._splash.gaugeHeight)/2)+1),gbox.getScreenW()-4,gbox._splash.gaugeHeight-2);
+1833tox.fillStyle=gbox._splash.gaugeColor;
+1834tox.fillRect(1,Math.floor(((gbox.getScreenH()-gbox._splash.gaugeHeight)/2)+1),(bw>0?bw:0),gbox._splash.gaugeHeight-2);
+1835}
+1836tox.restore();
+1837gbox.setStatBar("Loading... ("+gbox._loaderqueue.getDone()+"/"+gbox._loaderqueue.getTotal()+")");
+1838setTimeout(gbox._waitforloaded,50);
+1839}else{
+1840gbox.deleteImage("_splash");
+1841gbox.setStatBar();
+1842gbox._cb();
+1843}
+1844},
+1845clearCache:function(){this._loadercache.clear();}
+1846
+1847};
+1848
+1849
\ No newline at end of file
diff --git a/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_help.js.html b/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_help.js.html
new file mode 100644
index 0000000..e20cd09
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_help.js.html
@@ -0,0 +1,547 @@
+
1// ---
+ 2// Copyright (c) 2010 Francesco Cottone, http://www.kesiev.com/
+ 3// ---
+ 4
+ 5/**
+ 6 * @namespace Help module provides some Javascript-specific functions, such object copying, randomizing functions,
+ 7 * string/array handlers and the akihabaraInit function.
+ 8 */
+ 9varhelp={
+ 10
+ 11/**
+ 12 * Generates numbers from st to ed, along with a skip value.
+ 13 * @param {Integer} st Starting number.
+ 14 * @param {Integer} ed Ending number.
+ 15 * @param {Integer} skip Number to increment by.
+ 16 * @returns An array containing the set of numbers from st to ed, incrementing by skip.
+ 17 */
+ 18seq:function(st,ed,skip){
+ 19varret=[];
+ 20for(vari=st;i<ed;i+=(skip==null?1:skip))ret.push(i);
+ 21returnret;
+ 22},
+ 23
+ 24/**
+ 25 * Multiplies two numbers together, returning the result, unless the first parameter is less than 2, in which case it returns 1.
+ 26 * @param {Float} v First value.
+ 27 * @param {Float} mul Second value.
+ 28 * @returns An integer, v*mul, unless v<2 in which case it returns 1.
+ 29 */
+ 30// Handle a multiplier like counter. that means, 0=1 / 1=1 / 2=2*mul etc...
+ 31multiplier:function(v,mul){
+ 32return(!v||(v<2)?1:v*(!mul?1:mul));
+ 33},
+ 34
+ 35/**
+ 36 * Prepends a string with repeated instances of another string until it the result is greater than or equal to a desired length.
+ 37 * @param {String} str The string you wish to modify.
+ 38 * @param {Integer} len The desired length of your resultant string.
+ 39 * @param {String} pad The string you wish to prepend to str.
+ 40 * @returns A string whose length is no greater than len+pad.length, with pad prepending str repeatedly.
+ 41 */
+ 42prepad:function(str,len,pad){
+ 43str+="";
+ 44while(str.length<len)str=pad+str;
+ 45returnstr;
+ 46},
+ 47
+ 48/**
+ 49 * Postpends a string with repeated instances of another string until it the result is greater than or equal to a desired length.
+ 50 * @param {String} str The string you wish to modify.
+ 51 * @param {Integer} len The desired length of your resultant string.
+ 52 * @param {String} pad The string you wish to postpend to str.
+ 53 * @returns A string whose length is no greater than len+pad.length, with pad postpending str repeatedly.
+ 54 */
+ 55postpad:function(str,len,pad){
+ 56str+="";
+ 57while(str.length<len)str+=pad;
+ 58returnstr;
+ 59},
+ 60
+ 61/**
+ 62 * Tests to see if an object is being "jumped on" by another object. Only works for platformers, since it assumes accy>0 means you're falling onto something else.
+ 63 * @param {Object} th The object that is (possibly) being jumped on.
+ 64 * @param {Object} by The object doing the jumping-on.
+ 65 * @returns True if the two objects are overlapping enough and by.accy>0.
+ 66 */
+ 67isSquished:function(th,by){
+ 68return((by.accy>0)&&gbox.collides(th,by)&&(Math.abs(th.y-(by.y+by.h))<(th.h/2)))
+ 69},
+ 70
+ 71/**
+ 72 * Generates uniformly distributed random integers between min and min+range, non-inclusive. So help.random(0,2) will only return 0 and 1, etc.
+ 73 * @param {Integer} min The minimum random value to be returned by the function.
+ 74 * @param {Integer} range The number of different values returned by the function.
+ 75 * @returns An integer between min (includive) and min+range (noninclusive).
+ 76 */
+ 77random:function(min,range){
+ 78returnmin+Math.floor(Math.random()*range);
+ 79},
+ 80
+ 81
+ 82/**
+ 83 * Determines which frame of a given animation to display. Will loop an animation.
+ 84 * @param {Integer} cnt A global frame counter.
+ 85 * @param {Object} anim An object with parameters speed (the animation speed) and frames (the array representing the animation sequence).
+ 86 * @returns The particular animation frame to display during this step.
+ 87 */
+ 88decideFrame:function(cnt,anim){
+ 89returnanim.frames[Math.floor(cnt/anim.speed)%anim.frames.length];
+ 90},
+ 91
+ 92/**
+ 93 * Determines which frame of a given animation to display. Will remain on the last frame when the animation has played once.
+ 94 * @param {Integer} cnt A global frame counter.
+ 95 * @param {Object} anim An object with parameters speed (the animation speed) and frames (the array representing the animation sequence).
+ 96 * @returns The particular animation frame to display during this step.
+ 97 */
+ 98decideFrameOnce:function(cnt,anim){
+ 99returnanim.frames[(cnt>=anim.frames.length*anim.speed?anim.frames.length-1:Math.floor(cnt/anim.speed))];
+100},
+101
+102/**
+103 * Returns whether the animation was fully played at least once with decideFrame or fully with decideFrameOnce.
+104 * @param {Integer} cnt A global frame counter.
+105 * @param {Object} anim An object with parameters speed (the animation speed) and frames (the array representing the animation sequence).
+106 * @returns A boolean, true if the animation has been played at least once.
+107 */
+108isLastFrameOnce:function(cnt,anim){
+109return(cnt>=anim.frames.length*anim.speed);
+110},
+111
+112/**
+113 * Given an incrementing value each step, this will return a value increasing from 0 until max/2, at which point it will decrement to 0, then go back up to max/2, in an endless cycle.
+114 * @param {Integer} counter A counter.
+115 * @param {Integer} max This determines the period of the function -- assuming counter is incrementing by one, a complete back-and-forth will take 'max' steps.
+116 * @returns An integer.
+117 */
+118upAndDown:function(counter,max){
+119if((counter%max)>(max/2))returnmax-(counter%max);elsereturn(counter%max);
+120},
+121
+122/**
+123 * Given x,y coordinates and map information, this returns the tile at a given point.
+124 * @param {Integer} x An x-coordinate.
+125 * @param {Integer} y A y-coordinate.
+126 * @param {Object} map The map object.
+127 * @param {Object} ifout An object or value to be returned if the x,y coordinate pair is outside the map.
+128 * @param {String} mapid The id for the map array within the map object. Default is 'map'.
+129 * @returns An integer representing the value of the tile in the map array at that x,y coordinate. If there is no tile, null is returned.
+130 */
+131getTileInMap:function(x,y,map,ifout,mapid){
+132if(!mapid)mapid="map";
+133varts=gbox._tiles[map.tileset];
+134vartx=Math.floor(x/ts.tilew);
+135varty=Math.floor(y/ts.tileh);
+136if((ty<0)||(ty>=map[mapid].length))returnifout;else
+137if((tx<0)||(tx>=map[mapid][ty].length))returnifout;else
+138returnmap[mapid][ty][tx];
+139},
+140
+141/**
+142 * Takes an ascii-art-style array of characters and converts it to an Akihabara-compatible map format.
+143 * @param {Array} map An array of characters representing a map.
+144 * @param {Array} tra A translation array. This is an array of arrays, formatted like [ [null, char1], [0, char2], [1, char3] ]. There must at least be a null entry, followed by one numerical entry for each tile type you want to render, corresponding to the unique characters in the map array. The null entry maps a character to empty space.
+145 * @returns A map array formatted such that it can be attached to a map object.
+146 */
+147asciiArtToMap:function(map,tra){
+148varsz=tra[0][1].length;
+149varret=[];
+150varxpos;
+151varpie;
+152for(vary=0;y<map.length;y++){
+153varrow=[];
+154xpos=0;
+155while(xpos<map[y].length){
+156pie=map[y].substr(xpos,sz);
+157for(vart=0;t<tra.length;t++)
+158if(pie==tra[t][1]){
+159if(t==0)row.push(null);elserow.push(tra[t][0]);
+160break;
+161}
+162xpos+=sz;
+163}
+164ret.push(row);
+165}
+166returnret;
+167},
+168
+169/**
+170 * Calculates and sets the width and height (map.h, map.w) and half-width and half-height (map.hh, map.hw) of a map object.
+171 * @param {Object} map A map object, containing a map array and a tileset array.
+172 * @returns A map object with map.w, map.h, map.hh, and map.hw set correctly.
+173 */
+174// Finalize a map definition, setting height and width in pixels etc.
+175// Args: (map)
+176// Outs: finalized map
+177finalizeTilemap:function(map){
+178varts=gbox._tiles[map.tileset];
+179map.h=map.map.length*ts.tileh;
+180map.w=map.map[0].length*ts.tilew;
+181map.hw=Math.floor(map.w/2);
+182map.hh=Math.floor(map.h/2);
+183returnmap;
+184},
+185
+186/**
+187 * Converts an x-coordinate of a pixel to its corresponding tile x-coordinate.
+188 * @param {Object} map A map object, containing a map array and a tileset array.
+189 * @param {Integer} x An x-coordinate.
+190 * @param {Integer} gap (Not used.)
+191 * @returns A map object with map.w, map.h, map.hh, and map.hw set correctly.
+192 */
+193xPixelToTileX:function(map,x,gap){
+194varts=gbox._tiles[map.tileset];
+195returnMath.floor(x/ts.tilew);
+196},
+197
+198/**
+199 * Converts a y-coordinate of a pixel to its corresponding tile y-coordinate.
+200 * @param {Object} map A map object, containing a map array and a tileset array.
+201 * @param {Integer} y A y-coordinate.
+202 * @param {Integer} gap (Not used.)
+203 * @returns A map object with map.w, map.h, map.hh, and map.hw set correctly.
+204 */
+205yPixelToTileY:function(map,y,gap){
+206varts=gbox._tiles[map.tileset];
+207returnMath.floor(y/ts.tileh);
+208},
+209
+210/**
+211 * Converts an x-coordinate of a pixel to the x-coordinate of the tile column it's in. This effectively "snaps" an x coordinate to a tile edge.
+212 * @param {Object} map A map object, containing a map array and a tileset array.
+213 * @param {Integer} x An x-coordinate.
+214 * @param {Integer} gap Number of pixels gap in tilemap. Default is 0.
+215 * @returns The x-coordinate in pixels of the tile column.
+216 */
+217xPixelToTile:function(map,x,gap){
+218varts=gbox._tiles[map.tileset];
+219return(Math.floor(x/ts.tilew)+(gap?gap:0))*ts.tilew;
+220},
+221
+222/**
+223 * Converts a y-coordinate of a pixel to the y-coordinate of the tile row it's in. This effectively "snaps" a y coordinate to a tile edge.
+224 * @param {Object} map A map object, containing a map array and a tileset array.
+225 * @param {Integer} y A y-coordinate.
+226 * @param {Integer} gap Number of pixels gap in tilemap. Default is 0.
+227 * @returns The y-coordinate in pixels of the tile row.
+228 */
+229yPixelToTile:function(map,y,gap){
+230varts=gbox._tiles[map.tileset];
+231return(Math.floor(y/ts.tileh)+(gap?gap:0))*ts.tileh;
+232},
+233
+234/**
+235 * Limits a number to a certain range. If the number is below the minimum, the minimum is returned. If the number is above the maximum, the maximum is returned.
+236 * @param {Float} v A value.
+237 * @param {Float} min The minimum limit.
+238 * @param {Float} max The maximum limit.
+239 * @returns A value equal to v if min<v<max. Returns min if v<min, max if v>max.
+240 */
+241limit:function(v,min,max){
+242if(v<min)returnmin;elseif(v>max)returnmax;elsereturnv;
+243},
+244
+245/**
+246 * Subtracts or adds 1 to a value, always converging to zero. For example, passing -3 yields -2, 5 yields 4, etc. Works best with integers.
+247 * @param {Integer} v A value.
+248 * @returns A value that is one closer to 0 on the number line than v.
+249 */
+250goToZero:function(v){return(v?v-(v/Math.abs(v)):0);},
+251
+252/**
+253 * Merges two sets of parameters together without overwriting existing parameters. This merges from model to data, and if data and model share parameters, data's values remain intact.
+254 * @param {Object} data An object containing a set of parameters, the destination of the merge.
+255 * @param {Object} model An object containing a set of parameters, the source of the merge.
+256 * @returns A merged model where the values of 'data' remain untouched: only new parameters and values from 'model' make it in.
+257 * @example
+258 * dst = {a: 1, b: 2, c: "three"};
+259 * src = {c: "three", d: "four"};
+260 * merged = help.mergeWithModel(dst,src);
+261 * merged; // => {a: 1, b: 2, c: 3, d: "four"};
+262 */
+263mergeWithModel:function(data,model){
+264if(data==null)data={};
+265if(model!=null)
+266for(variinmodel)
+267if(data[i]==null)data[i]=model[i];
+268returndata;
+269},
+270
+271/**
+272 * Merges two sets of parameters together overwriting any existing parameters. This merges model->data, and if data and model share parameters, data's are overwritten by model's.
+273 * @param {Object} data An object containing a set of parameters, the destination of the merge.
+274 * @param {Object} model An object containing a set of parameters, the source of the merge.
+275 * @returns A merged model where the values of 'model' take precedence over those of 'data'. The 'data' object is returned and will be an exact copy of 'model', plus any parameters that 'data' had before the merge that 'model' did not.
+276 * @example
+277 * dst = {a: 1, b: 2, c: "three"};
+278 * src = {c: "three", d: "four"};
+279 * merged = help.mergeWithModel(dst,src);
+280 * merged; // => {a: 1, b: 2, c: "three", d: "four"}
+281 */
+282copyModel:function(data,model){
+283if(data==null)data={};
+284if(model!=null)
+285for(variinmodel)data[i]=model[i];
+286returndata;
+287},
+288
+289/**
+290 * Creates a subset of an existing set of parameters.
+291 * @param {Object} obj An object containing a set of parameters, the source of the data.
+292 * @param {Array} attrs An array of strings, containing the names of parameters you wish to copy.
+293 * @returns A new set of parameters based on the subset specified.
+294 * @example
+295 * data = {a: 1, b: 2, c: "three"};
+296 * newdata = help.createModel(data, ["a", "c"]);
+297 * newdata; // => {a: 1, c: "three"}
+298 */
+299createModel:function(obj,attrs){
+300varret={};
+301for(vari=0;i<attrs.length;i++)ret[attrs[i]]=obj[attrs[i]];
+302returnret;
+303},
+304
+305/**
+306 * Creates a duplicate of an existing set of parameters.
+307 * @param {Object} model An object containing a set of parameters.
+308 * @returns A new object, equivalent to 'model'.
+309 * @example
+310 * data = {a: 1, b: 2, c: "three"};
+311 * newdata = help.cloneObject(data);
+312 * newdata; // => {a: 1, b: 2, c: "three"}
+313 */
+314cloneObject:function(model){
+315if(!model)returnmodel;
+316vardata={};
+317for(variinmodel)data[i]=model[i];
+318returndata;
+319},
+320
+321/**
+322 * Sets a tile in the map and draws it. Does not return anything.
+323 * @param {Object} ctx The canvas context for the map. Accessed via gbox.getCanvasContext("canvasname")
+324 * @param {Object} map The game map object.
+325 * @param {Integer} x The index of the tile column within the map array -- so a 1 would mean the second column of tiles.
+326 * @param {Integer} y The index of the tile row within the map array -- so a 1 would mean the second row of tiles.
+327 * @param {Integer} tile The integer representing the new tile you wish to draw. This is its index within the tileset; a null value will erase whatever tile is present.
+328 * @param {String} The ID of the map. Defaults to 'map'.
+329 * @example
+330 * // Remove the second tile to the right and down from the upper left corner of the tile map. Assumes our map canvas is called 'map_canvas'.
+331 * help.setTileInMap(gbox.getCanvasContext("map_canvas"),map,1,1,null,"map");
+332 */
+333setTileInMap:function(ctx,tilemap,x,y,tile,map){
+334varts=gbox.getTiles(tilemap.tileset);
+335tilemap[(map==null?"map":map)][y][x]=tile;
+336if(tile==null)
+337gbox.blitClear(ctx,{x:x*ts.tilew,y:y*ts.tilew,h:ts.tileh,w:ts.tilew});
+338else
+339gbox.blitTile(ctx,{tileset:tilemap.tileset,tile:tile,dx:x*ts.tilew,dy:y*ts.tilew});
+340},
+341
+342
+343/**
+344 * Returns the Nth element in an array. If the array is shorter than N, it returns the last element of the array.
+345 * @param {Array} a An array.
+346 * @param {Integer} id An index to the array.
+347 * @returns If id > a.length, it returns a[a.length-1]. Otherwise returns a[id].
+348 */
+349getArrayCapped:function(a,id){
+350if(id>=a.length)returna[a.length-1];elsereturna[id];
+351},
+352
+353// Get an item of an array of object, using a field as index. is returned the first entry if the field is not valued.
+354getArrayIndexed:function(a,value,field){
+355if(a[0][field]==null)returna[0];
+356vari=0;
+357while((value>a[i][field])&&(i!=a.length-1))i++;
+358returna[i];
+359},
+360
+361
+362/**
+363 * Converts a quantity of frames into a timestamp formatted "mm:ss:cs" (minutes, seconds, centiseconds). Calculated using the current frames per second.
+364 * @param {Integer} frames A quantity of frames.
+365 * @returns A string containing a timestamp formatted "mm:ss:cs", representing the length of time it would take to render that many frames.
+366 * @example
+367 * // Assuming 25 frames per second, Akihabara's default.
+368 * timestamp = help.framestotime(25);
+369 * timestamp; // => '00:01:00';
+370 * timestamp = help.framestotime(25 * 60);
+371 * timestamp; // => '01:00:00';
+372 */
+373framestotime:function(frames){
+374varcsec=Math.ceil(frames/gbox.getFps()*100);
+375returnthis.prepad((Math.floor(csec/6000)%60),2,"0")+":"+this.prepad((Math.floor(csec/100)%60),2,"0")+":"+this.prepad(csec%100,2,"0");
+376
+377},
+378
+379/**
+380 * Reads the value of a query parameter from the URL of the web page.
+381 * @param {String} name The name of the URL parameter.
+382 * @returns The value of the URL parameter, as a string.
+383 * @example
+384 * // If the URL is http://example.com/game.html?lives=3
+385 * player.lives = help.geturlparameter("lives");
+386 * player.lives; // => 3
+387 */
+388geturlparameter:function(name){
+389name=name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
+390varregexS="[\\?&]"+name+"=([^]*)";
+391varregex=newRegExp(regexS);
+392varresults=regex.exec(window.location.href);
+393if(results==null)
+394return"";
+395else
+396returnresults[1];
+397},
+398
+399/**
+400 * Writes the contents of an object to a string. Useful for debugging.
+401 * @param {Object} Any object.
+402 * @returns A string containing all the contents of an object. If the object contains functions, the string will contain the code for those functions.
+403 */
+404objToStr:function(o){
+405varret="";
+406for(varnino)ret+=n+":["+o[n]+"] ";
+407returnret;
+408},
+409
+410/**
+411 * Tests whether an object contains a given parameter.
+412 * @param {Object} A reference to a parameter of an object.
+413 * @returns True if the object contains that parameter, false if it does not.
+414 * @example
+415 * foo = {a: 1, b: 2};
+416 * help.isDefined(foo.a); // => true
+417 * help.isDefined(foo.c); // => false
+418 */
+419isDefined:function(v){
+420return((typeof(v)!=='undefined')||(v===null));
+421},
+422
+423/**
+424 * Automatically configures a bunch of settings depending on the web browser and device that is viewing the game. Mostly sets the maximum number of audio channels and touch settings.
+425 */
+426getDeviceConfig:function(){
+427
+428varcap;
+429if(navigator.userAgent.match(/iPhone/i)||navigator.userAgent.match(/iPod/i)||navigator.userAgent.match(/Android/i))
+430cap={touch:true,width:320};
+431elseif(navigator.userAgent.match(/iPad/i))
+432cap={touch:true,width:768,forcedidle:10};// Forced idle time is needed for correct framerate calculation.
+433else
+434cap={zoom:2};
+435
+436cap.canaudio=!!(document.createElement('audio').canPlayType);
+437
+438if(cap.canaudio){
+439if(navigator.userAgent.match(/iPad/i)||navigator.userAgent.match(/iPhone/i)||navigator.userAgent.match(/iPod/i)){
+440cap.audiocompatmode=2;// Single audio per time, so compatibility mode is needed. Plays only the "bgmusic" channel.
+441cap.audioteam=1;// Only a member is required in the audioteam.
+442cap.audioisexperimental=true;// Audio is experimental, since limited.
+443}elseif(navigator.userAgent.match(/Chrome/i)){
+444cap.audioteam=3;// Quite low performance on playback responsiveness.
+445}elseif(navigator.userAgent.match(/Firefox/i)){
+446cap.audioteam=1;// Testing smaller audioteam
+447cap.audiopositiondelay=0.3;// Ogg playback is slower 0.3 between MP3 playback. Don't know why :)
+448cap.audiocreatemode=1;// Firefox is stalling while downloading lot of things
+449}elseif(navigator.userAgent.match(/Minefield/i)){
+450cap.audioteam=1;// Testing smaller audioteam
+451cap.audiocreatemode=1;// Firefox is stalling while downloading lot of things
+452// Minefield has fixed the 0.3 delay!
+453}elseif(navigator.userAgent.match(/Safari/i)){
+454cap.audioteam=1;// Testing smaller audioteam
+455}elseif(navigator.userAgent.match(/Opera/i)){
+456cap.audioteam=1;// Testing smaller audioteam
+457cap.audiocreatemode=1;// Do not like audio object cloning very much
+458}else
+459cap.audioisexperimental=true;// Audio is just experimental on all other devices.
+460
+461}
+462
+463returncap;
+464},
+465
+466/**
+467 * This provides a number of configurations: fps, display zoom, dynamic frameskip, force touch parameters, etc. Many of these settings can
+468 * be set manually by passing an object with the parameters defined, or via URL parameters.
+469 * @param {Object} data An optional object containing parameters you wish to set. Works for data.zoom, data.splash, data.width, data.height, data.title, data.fps, and data.padmode.
+470 */
+471akihabaraInit:function(data){
+472if((typeofdata).toLowerCase()=="string")data={title:data};
+473vardevice=this.getDeviceConfig();
+474varfootnotes=["MADE WITH AKIHABARA (C)2010 - GPL2/MIT","Project: www.kesiev.com/akihabara","Sources: github.com/kesiev/akihabara"];
+475document.title=(data.title?data.title:"Akihabara");
+476if(data.splash){
+477if(data.splash.footnotes)
+478for(vari=0;i<footnotes.length;i++)data.splash.footnotes.push(footnotes[i]);
+479gbox.setSplashSettings(data.splash);
+480}
+481varscreenwidth=(data.width?data.width:(data.portrait?240:320));
+482varscreenheight=(data.height?data.height:(data.portrait?320:240));
+483if(!data.splash||(data.splash.minilogo==null))gbox.setSplashSettings({minilogo:"logo"});
+484if(!data.splash||(data.splash.background==null))gbox.setSplashSettings({background:"akihabara/splash.png"});
+485if(!data.splash||(data.splash.minimalTime==null))gbox.setSplashSettings({minimalTime:3000});
+486if(!data.splash||(data.splash.footnotes==null))gbox.setSplashSettings({footnotes:footnotes});
+487document.body.style.backgroundColor="#000000";
+488gbox.setScreenBorder(false);
+489if(help.geturlparameter("statusbar"))gbox.setStatusBar(1);
+490if(help.geturlparameter("db"))gbox.setDoubleBuffering(true);
+491if(help.geturlparameter("noautoskip"))gbox.setAutoskip(null);
+492if(help.geturlparameter("zoom"))gbox.setZoom(help.geturlparameter("zoom"));else
+493if(help.isDefined(data.zoom))gbox.setZoom(data.zoom);else
+494if(help.isDefined(device.zoom))gbox.setZoom(device.zoom);else
+495if(help.isDefined(device.width))gbox.setZoom(device.width/screenwidth);
+496if(help.geturlparameter("fps"))gbox.setFps(help.geturlparameter("fps")*1);
+497elsegbox.setFps((data.fps?data.fps:25));
+498if(help.geturlparameter("fskip"))gbox.setFrameskip(help.geturlparameter("fskip"));
+499if(help.geturlparameter("forcedidle"))gbox.setForcedIdle(help.geturlparameter("forcedidle")*1);
+500elseif(help.isDefined(device.forcedidle))gbox.setForcedIdle(device.forcedidle);
+501if(help.geturlparameter("canlog"))gbox.setCanLog(true);
+502
+503gbox.initScreen(screenwidth,screenheight);
+504
+505if(help.geturlparameter("showplayers"))gbox.setShowPlayers(help.geturlparameter("showplayers")=="yes");
+506if(help.geturlparameter("canaudio"))gbox.setCanAudio(help.geturlparameter("canaudio")=="yes");else
+507gbox.setCanAudio(device.canaudio&&(!device.audioisexperimental||gbox.getFlag("experimental")));
+508if(help.geturlparameter("audiocompatmode"))gbox.setAudioCompatMode(help.geturlparameter("audiocompatmode")*1);else
+509if(help.isDefined(device.audiocompatmode))gbox.setAudioCompatMode(device.audiocompatmode);
+510if(help.geturlparameter("audioteam"))gbox.setAudioTeam(help.geturlparameter("audioteam")*1);else
+511if(help.isDefined(device.audioteam))gbox.setAudioTeam(device.audioteam);
+512if(help.geturlparameter("loweraudioteam"))gbox.setLowerAudioTeam(help.geturlparameter("loweraudioteam")*1);else
+513if(help.isDefined(device.loweraudioteam))gbox.setLowerAudioTeam(device.loweraudioteam);
+514if(help.geturlparameter("audiocreatemode"))gbox.setAudioCreateMode(help.geturlparameter("audiocreatemode")*1);else
+515if(help.isDefined(device.audiocreatemode))gbox.setAudioCreateMode(device.audiocreatemode);
+516if(help.geturlparameter("audiodequeuetime"))gbox.setAudioDequeueTime(help.geturlparameter("audiodequeuetime")*1);else
+517if(help.isDefined(device.audiodequeuetime))gbox.setAudioDequeueTime(device.audiodequeuetime);
+518if(help.geturlparameter("audiopositiondelay"))gbox.setAudioPositionDelay(help.geturlparameter("audiopositiondelay")*1);else
+519if(help.isDefined(device.audiopositiondelay))gbox.setAudioPositionDelay(device.audiopositiondelay);
+520
+521
+522
+523if(help.geturlparameter("touch")=="no");
+524elseif((help.geturlparameter("touch")=="yes")||device.touch)
+525switch(data.padmode){
+526case"fretboard":{
+527iphofretboard.initialize({h:100,bg:"akihabara/fretboard.png"});
+528break;
+529}
+530case"none":{
+531break;
+532}
+533default:{
+534iphopad.initialize({h:100,dpad:"akihabara/dpad.png",buttons:"akihabara/buttons.png",bg:"akihabara/padbg.png"});
+535break;
+536}
+537}
+538}
+539}
+540
\ No newline at end of file
diff --git a/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_iphofretboard.js.html b/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_iphofretboard.js.html
new file mode 100644
index 0000000..74561f9
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_iphofretboard.js.html
@@ -0,0 +1,65 @@
+
\ No newline at end of file
diff --git a/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_iphopad.js.html b/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_iphopad.js.html
new file mode 100644
index 0000000..afaf85a
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_iphopad.js.html
@@ -0,0 +1,128 @@
+
\ No newline at end of file
diff --git a/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_tool.js.html b/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_tool.js.html
new file mode 100644
index 0000000..82c57bc
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_tool.js.html
@@ -0,0 +1,139 @@
+
1// ---
+ 2// Copyright (c) 2010 Francesco Cottone, http://www.kesiev.com/
+ 3// ---
+ 4
+ 5/**
+ 6 * @namespace Tool module provides simple developing tools. Currently, this file only has a cel-composer:
+ 7 * it can compose an image stacking a set of frames for animating objects, applying a
+ 8 * number of filters to each frame.
+ 9 */
+ 10vartool={
+ 11_images:[],
+ 12_loadedflag:[],
+ 13_data:{},
+ 14_count:0,
+ 15_countloaded:0,
+ 16
+ 17/**
+ 18 * This function documents that an image in an animation sequence is loaded and checks if the other images are loaded or not
+ 19 * @param {Object} id This is the object which is used as an id for keeping track of things related to this object in this function
+ 20 */
+ 21_loaded:function(id){
+ 22this._loadedflag[id]=true;
+ 23tool._countloaded++;
+ 24document.title=tool._countloaded+"/"+tool._count;
+ 25for(vari=0;i<this._images.length;i++)
+ 26if(!this._loadedflag[i])document.title+=this._images[i].src+", ";
+ 27},
+ 28
+ 29/**
+ 30 * This checks that everything being kept track of with _count is loaded and depending on the result calls
+ 31 */
+ 32_loadall:function(){
+ 33if(tool._count!=tool._countloaded)
+ 34setTimeout(tool._loadall,1000);
+ 35else
+ 36tool._allloaded();
+ 37},
+ 38
+ 39/**
+ 40 * This makes the image cells for an animation and adds the load event listeners that the other stuff work to them. Calls loadall at the end.
+ 41 * @param {Object} data This is the created animation data being passed in to be used by the function.
+ 42 */
+ 43makecels:function(data){
+ 44this._data=data;
+ 45varid=0;
+ 46for(varr=0;r<data.rows.length;r++){
+ 47for(vari=0;i<data.rows[r].length;i++){
+ 48this._images[id]=newImage();
+ 49this._images[id].addEventListener('load',function(){tool._loaded(this.id)},false);
+ 50this._images[id].setAttribute("id",id);
+ 51this._images[id].src=data.rows[r][i].img;
+ 52this._count++;
+ 53id++;
+ 54}
+ 55}
+ 56this._loadall();
+ 57},
+ 58
+ 59/**
+ 60 * @function
+ 61 * Creates and initializes the Canvas element. Is called from makecels. This function requires that this._data have been instantiated prior to function call.
+ 62 */
+ 63_allloaded:function(){
+ 64vardata=this._data;
+ 65varwid=0;
+ 66varhei=0;
+ 67varcurwid=0;
+ 68varid=0;
+ 69for(varr=0;r<data.rows.length;r++){
+ 70hei+=this._images[id].height*1;
+ 71curwid=0;
+ 72for(vari=0;i<data.rows[r].length;i++){curwid+=this._images[id].width*1;id++}
+ 73if(wid<curwid)wid=curwid;
+ 74}
+ 75
+ 76varcels=document.createElement("canvas");
+ 77cels.style.border="1px solid red";
+ 78cels.setAttribute('height',hei);
+ 79cels.setAttribute('width',wid);
+ 80document.body.appendChild(cels);
+ 81varctx=cels.getContext("2d");
+ 82
+ 83varcurx=0;
+ 84varcury=0;
+ 85id=0;
+ 86for(varr=0;r<data.rows.length;r++){
+ 87curx=0;
+ 88for(vari=0;i<data.rows[r].length;i++){
+ 89ctx.drawImage(this._images[id],curx,cury);
+ 90if(data.rows[r][i].filter){
+ 91if(data.rows[r][i].filter){
+ 92varimgd=ctx.getImageData(curx,cury,this._images[id].width,this._images[id].height);
+ 93varpix=imgd.data;
+ 94
+ 95// Loop over each pixel and invert the color.
+ 96for(varz=0,n=pix.length;z<n;z+=4){
+ 97if(data.rows[r][i].filter.replace){
+ 98for(varw=0;w<data.rows[r][i].filter.replace.length;w++){
+ 99
+100repl=data.rows[r][i].filter.replace[w].from;
+101to=data.rows[r][i].filter.replace[w].to;
+102if((pix[z]==repl.r)&&(pix[z+1]==repl.g)&&(pix[z+2]==repl.b)&&(pix[z+3]==repl.a)){
+103pix[z]=to.r;
+104pix[z+1]=to.g;
+105pix[z+2]=to.b;
+106pix[z+3]=to.a;
+107}
+108}
+109}
+110if(data.rows[r][i].filter.color&&(pix[z+3]!=0)){
+111pix[z]=data.rows[r][i].filter.color.r;
+112pix[z+1]=data.rows[r][i].filter.color.g;
+113pix[z+2]=data.rows[r][i].filter.color.b;
+114pix[z+3]=data.rows[r][i].filter.color.a;
+115}
+116
+117// i+3 is alpha (the fourth element)
+118}
+119ctx.putImageData(imgd,curx,cury);
+120
+121}
+122}
+123curx+=this._images[id].width*1;
+124id++;
+125}
+126cury+=this._images[id-1].height*1;
+127}
+128
+129}
+130
+131}
+132
\ No newline at end of file
diff --git a/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_toys.js.html b/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_toys.js.html
new file mode 100644
index 0000000..bdb3f39
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_toys.js.html
@@ -0,0 +1,2449 @@
+
1// ---
+ 2// Copyright (c) 2010 Francesco Cottone, http://www.kesiev.com/
+ 3// ---
+ 4
+ 5/**
+ 6 * @namespace
+ 7 * Toys module provides lots of common routines during the game developing:
+ 8 * from effects for screen titles to HUD handling to platform/SHMUP/RPG oriented routines,
+ 9 * like jumping characters, Z-Indexed objects, bullets, sparks, staff rolls, bonus screens, dialogues etc.
+ 10 */
+ 11vartoys={
+ 12
+ 13// CONSTANTS
+ 14NOOP:function(){},
+ 15PUSH_NONE:0,
+ 16PUSH_LEFT:1,
+ 17PUSH_RIGHT:2,
+ 18PUSH_UP:3,
+ 19PUSH_DOWN:4,
+ 20
+ 21FACES:["up","right","down","left"],
+ 22FACES_ANGLE:[trigo.ANGLE_UP,trigo.ANGLE_RIGHT,trigo.ANGLE_DOWN,trigo.ANGLE_LEFT],
+ 23FACE_UP:0,
+ 24FACE_RIGHT:1,
+ 25FACE_DOWN:2,
+ 26FACE_LEFT:3,
+ 27
+ 28/**
+ 29 * @namespace
+ 30 * Top-view RPG specific libraries.
+ 31 */
+ 32topview:{
+ 33
+ 34/**
+ 35 * Checks if an object checks that both objects are on the same Z plane and if so it calls gbox.collides.
+ 36 * @param {Object} fr The object which collision is being checked for.
+ 37 * <ul>
+ 38 * <li>x{Integer}: (required)Objects x position</li>
+ 39 * <li>y{Integer}: (required)Objects y position</li>
+ 40 * <li>z{Integer}: (required)Objects z position</li>
+ 41 * <li>colx{Integer}: (required)The dimension of the collision box along the x axis</li>
+ 42 * <li>coly{Integer}: (required)The dimension of the collision box along the y axis</li>
+ 43 * <li>colh{Integer}: (required)Collision box height</li>
+ 44 * <li>colw{Integer}: (required)Collision box width</li>
+ 45 * </ul>
+ 46 * @param {Object} to The object that collision is being checked against.
+ 47 * <ul>
+ 48 * <li>x{Integer}: (required)Objects x position</li>
+ 49 * <li>y{Integer}: (required)Objects y position</li>
+ 50 * <li>z{Integer}: (required)Objects z position</li>
+ 51 * <li>colx{Integer}: (required)Collision x</li>
+ 52 * <li>coly{Integer}: (required)Collision y</li>
+ 53 * <li>colh{Integer}: (required)Collision box height</li>
+ 54 * <li>colw{Integer}: (required)Collision box width</li>
+ 55 * </ul>
+ 56 * @param {int} t This is the tollerance (or margin for error) on the collide function.
+ 57 */
+ 58collides:function(fr,to,t){// Special collision. Counts also the Z
+ 59if(Math.abs(fr.z,to.z)<5)returngbox.collides({x:fr.x+fr.colx,y:fr.y+fr.coly,h:fr.colh,w:fr.colw},{x:to.x+to.colx,y:to.y+to.coly,h:to.colh,w:to.colw},t);elsereturnfalse;
+ 60},
+ 61
+ 62/**
+ 63 * Checks for pixel collisions with an offset to the X and Y of the colidable using colx and coly.
+ 64 * @param {Object} fr The object which collision is being tested for.
+ 65 * @param {Object} to The object (or point) which collision is being tested against.
+ 66 * @param {int} t The tollerance of the collision algorithm.
+ 67 */
+ 68pixelcollides:function(fr,to,t){// Special collision. Counts also the Z
+ 69returngbox.pixelcollides(fr,{x:to.x+to.colx,y:to.y+to.coly,h:to.colh,w:to.colw},t);
+ 70},
+ 71
+ 72/**
+ 73 * Initializes the game with the variables needed for topview and whatever else you feed in through data.
+ 74 * @param {Object} th Passes in the object being initialized.
+ 75 * @param {Object} data This is used to pass in everything that's being initiliized. If a value is not in Data then a default value is used instead. This can pass in values which do not have a default.
+ 76 * <ul>
+ 77 * <li>x{Integer}: x position of the object. (defaults to 0)</li>
+ 78 * <li>y{Integer}: y position of the object. (defaults to 0)</li>
+ 79 * <li>z{Integer}: z index of the object. (defaults to 0)</li>
+ 80 * <li>accx{Integer}: The starting x velociyt of the object. (defaults to 0)</li>
+ 81 * <li>accy{Integer}: The starting y velocity of the object. (defaults to 0)</li>
+ 82 * <li>accz{Integer}: The starting z velocity of the object. (defaults to 0)</li>
+ 83 * <li>frames{Object}: This is stores the animation frames for the objects in a map style structure. An empty map means the default image will display with no animation frames. (defaults to an empty map)</li>
+ 84 * <li>shadow: (defaults to null)</li> //incomplete
+ 85 * <li>maxacc{Integer}: (defaults to )4</li>
+ 86 * <li>controlmaxacc{Integer}: (defaults to 4)</li>
+ 87 * <li>responsive: (defaults to 0)</li>
+ 88 * <li>weapon: (defaults to 0)</li>
+ 89 * <li>camera{Boolean}: (defaults to true)</li>
+ 90 * <li>flipv{Boolean}: Notes if the object is flipped vertically(defaults to false)</li>
+ 91 * <li>fliph{Boolean}: Notes if the object is flipped horrizontally(defaults to false)</li>
+ 92 * <li>facing{Integer}: Stores the facing of the object. This is set with pre-defined Integer values from within Toys.(defaults to toys.FACE_DOWN)</li>
+ 93 * <ul>
+ 94 * <li>FACE_UP:0,</li>
+ 95 * <li>FACE_RIGHT:1,</li>
+ 96 * <li>FACE_DOWN:2,</li>
+ 97 * <li>FACE_LEFT:3,</li>
+ 98 * </ul>
+ 99 * <li>flipside{Boolean}: (defaults to true)</li>
+100 * <li>haspushing{Boolean}: (defaults to false)</li>
+101 * <li>frame: (default to 0)</li>
+102 * <li>colh{Integer}: (defaults to gbox.getTiles(th.tileset).tilehh)</li>
+103 * <li>colw{Integer}: (defaults to gbox.getTiles(th.tileset).tilew)</li>
+104 * <li>colx{Integer}: (defaults to 0)</li>
+105 * <li>staticspeed{Integer}: (defaults to 0)</li>
+106 * <li>nodiagonals{Boolean}: (defaults to false)</li>
+107 * <li>noreset: (defaults to false)</li>
+108 * </ul>
+109 */
+110initialize:function(th,data){
+111help.mergeWithModel(
+112th,
+113help.mergeWithModel(
+114data,
+115{
+116x:0,y:0,
+117z:0,
+118accx:0,accy:0,accz:0,
+119frames:{},
+120shadow:null,
+121maxacc:4,controlmaxacc:4,
+122responsive:0,// Responsiveness
+123weapon:0,// Weapon
+124camera:true,
+125flipv:false,fliph:false,
+126facing:toys.FACE_DOWN,
+127flipside:true,
+128haspushing:false,
+129frame:0,
+130colh:gbox.getTiles(th.tileset).tilehh,
+131colw:gbox.getTiles(th.tileset).tilew,
+132colx:0,
+133staticspeed:0,
+134nodiagonals:false,
+135noreset:false
+136}
+137)
+138);
+139if(th.coly==null)th.coly=gbox.getTiles(th.tileset).tileh-th.colh;
+140th.colhh=Math.floor(th.colh/2);
+141th.colhw=Math.floor(th.colw/2);
+142
+143toys.topview.spawn(th);
+144},
+145
+146/**
+147 * Spawns a new object in the topview namespace. This also merges parameters in data into paramaters in th using help.copyModel.
+148 * This initializes some basic basic variables for the object and sets the Z index.
+149 * @param {Object} th References 'this' which is the object that called the method (generally).
+150 * <ul>
+151 * <li>y {Integer}: (required) The object's y position.</li>
+152 * <li>h {Integer}: (required) The object's height.</li>
+153 * </ul>
+154 * @param {Object} data This holds variables to be merged into th's stored info.
+155 */
+156spawn:function(th,data){
+157th.xpushing=toys.PUSH_NONE;// user is moving side
+158th.vpushing=toys.PUSH_NONE;// user is moving side
+159th.zpushing=toys.PUSH_NONE;// user is moving side
+160th.counter=0;// self counter
+161th.hittimer=0;
+162th.killed=false;
+163help.copyModel(th,data);
+164gbox.setZindex(th,th.y+th.h);// these object follows the z-index and uses ZINDEX_LAYER
+165},
+166
+167/**
+168 * This sets and runs the control keys for the game.
+169 * @param {Object} th This is the object that is being controlled by the keys (assumed to be the player)
+170 * <ul>
+171 * <li>accx: the object's currect acceleration in the x direction</li>
+172 * <li>accy: the object's currect acceleration in the y direction</li>
+173 * <li>responsive: minimum movement speed</li>
+174 * <li>staticspeed: turns off acceleration</li>
+175 * <li>nodiagonals: boolean determining if the object can move along both axis at once.</li>
+176 * <li>xpushing: a boolean that notes whether the object is pushing against something in the x direction.</li>
+177 * <li>ypushing: a boolean that notes whether the object is pushing against something in the y direction.</li>
+178 * <li>controlmaxacc: max acceleration for the object along an axis</li>
+179 * <li>noreset: checks for the object being allowed to reset its pushing status (?)</li>
+180 * </ul>
+181 * @param {Object} keys These are the control keys being passed in for left, right, up, and down.
+182 * //incomplete
+183 */
+184controlKeys:function(th,keys){
+185varcancelx=false;
+186varcancely=false;
+187varidlex=false;
+188varidley=false;
+189
+190if(gbox.keyIsPressed(keys.left)||keys.pressleft){
+191th.xpushing=toys.PUSH_LEFT;
+192th.facing=toys.FACE_LEFT;
+193if(th.accx>th.responsive)th.accx=th.responsive;
+194if(th.staticspeed)th.accx=-th.staticspeed;elseth.accx=help.limit(th.accx-1,-th.controlmaxacc,th.controlmaxacc);
+195if(th.nodiagonals){cancely=true;idley=true}
+196}elseif(gbox.keyIsPressed(keys.right)||keys.pressright){
+197th.xpushing=toys.PUSH_RIGHT;
+198th.facing=toys.FACE_RIGHT;
+199if(th.accx<-th.responsive)th.accx=-th.responsive;
+200if(th.staticspeed)th.accx=th.staticspeed;elseth.accx=help.limit(th.accx+1,-th.controlmaxacc,th.controlmaxacc);
+201if(th.nodiagonals){cancely=true;idley=true}
+202}elseidlex=true;
+203
+204if(!cancely&&(gbox.keyIsPressed(keys.up)||keys.pressup)){
+205th.ypushing=toys.PUSH_UP;
+206th.facing=toys.FACE_UP;
+207if(th.accy>th.responsive)th.accy=th.responsive;
+208if(th.staticspeed)th.accy=-th.staticspeed;elseth.accy=help.limit(th.accy-1,-th.controlmaxacc,th.controlmaxacc);
+209if(th.nodiagonals){cancelx=true;idlex=true;}
+210}elseif(!cancely&&(gbox.keyIsPressed(keys.down)||keys.pressdown)){
+211th.ypushing=toys.PUSH_DOWN;
+212th.facing=toys.FACE_DOWN;
+213if(th.accy<-th.responsive)th.accy=-th.responsive;
+214if(th.staticspeed)th.accy=th.staticspeed;elseth.accy=help.limit(th.accy+1,-th.controlmaxacc,th.controlmaxacc);
+215if(th.nodiagonals){cancelx=true;idlex=true;}
+216}elseidley=true;
+217
+218
+219
+220if(idlex){
+221if(cancelx)th.accx=0;
+222if(cancelx||!th.noreset)th.xpushing=toys.PUSH_NONE;
+223}
+224if(idley){
+225if(cancely)th.accy=0;
+226if(cancely||!th.noreset)th.ypushing=toys.PUSH_NONE;
+227}
+228},
+229
+230/**
+231 * Gets the next X position the object is going to move to.
+232 * @param {Object} th The object being checked.
+233 * <ul>
+234 * <li>x: the current x position of the object</li>
+235 * <li>accx: the object's currect acceleration in the x direction</li>
+236 * <li>maxacc: the max accleration the object can have (if accx is greater than this then this value is used instead)</li>
+237 * </ul>
+238 */
+239getNextX:function(th){returnth.x+help.limit(th.accx,-th.maxacc,th.maxacc);},
+240
+241/**
+242 * Gets the next Y position the object is going to move to.
+243 * @param {Object} th The object being checked.
+244 * <ul>
+245 * <li>y: the current y position of the object</li>
+246 * <li>accy: the object's currect acceleration in the y direction</li>
+247 * <li>maxacc: the max accleration the object can have (if accy is greater than this then this value is used instead)</li>
+248 * </ul>
+249 */
+250getNextY:function(th){returnth.y+help.limit(th.accy,-th.maxacc,th.maxacc);},
+251
+252/**
+253 * Gets the next Z position the object is going to move to.
+254 * @param {Object} th The object being checked.
+255 * <ul>
+256 * <li>z: the current z position of the object</li>
+257 * <li>accz: the object's currect acceleration in the z direction</li>
+258 * <li>maxacc: the max accleration the object can have (if accz is greater than this then this value is used instead)</li>
+259 * </ul>
+260 */
+261getNextZ:function(th){returnth.z+help.limit(th.accz,-th.maxacc,th.maxacc);},
+262
+263/**
+264 * Sets the objects current location to its next location using the getNextX and getNextY methods.
+265 * @param {Object} th The object being modified.
+266 * <ul>
+267 * <li>x: the current x position of the object</li>
+268 * <li>y: the current y position of the object</li>
+269 * <li>accx: the object's currect acceleration in the x direction</li>
+270 * <li>accy: the object's currect acceleration in the y direction</li>
+271 * <li>maxacc: the max accleration the object can have (if either acceleration is greater than this then this value is used instead for that acceleration)</li>
+272 * </ul>
+273 */
+274applyForces:function(th){
+275th.x=toys.topview.getNextX(th);
+276th.y=toys.topview.getNextY(th);
+277},
+278
+279/**
+280 * This applies acceleration in the Z direction (not nessesarily gravity but whatever the next accerlation on the Z axis is)
+281 * @param {Object} th The object being modified.
+282 * <ul>
+283 * <li>z: the current z position of the object</li>
+284 * <li>accz: the object's currect acceleration in the z direction</li>
+285 * <li>maxacc: the max accleration the object can have (if accz is greater than this then this value is used instead)</li>
+286 * </ul>
+287 */
+288applyGravity:function(th){
+289th.z=toys.topview.getNextZ(th);
+290},
+291
+292/**
+293 * Degrades all accelerations on an object by one toward zero.
+294 * @param {Object} th The object being modified.
+295 * <ul>
+296 * <li>xpushing: a boolean that notes whether the object is pushing against something in the x direction.</li>
+297 * <li>ypushing: a boolean that notes whether the object is pushing against something in the y direction.</li>
+298 * <li>accx: the object's currect acceleration in the x direction</li>
+299 * <li>accy: the object's currect acceleration in the y direction</li>
+300 * </ul>
+301 */
+302handleAccellerations:function(th){
+303if(!th.xpushing)th.accx=help.goToZero(th.accx);
+304if(!th.ypushing)th.accy=help.goToZero(th.accy);
+305
+306},
+307
+308/**
+309 * Increases the Z acceleration on the object by one.
+310 * @param {Object} th The object being modified.
+311 * <ul>
+312 * <li>accz: the acceleration on the Z axis</li>
+313 * </ul>
+314 */
+315handleGravity:function(th){
+316th.accz++;
+317},
+318
+319/**
+320 * This sets which frame the object is going to display based on an agregate word that describes predefined states.
+321 * @param {Object} th The object whose frame is being set.
+322 * <ul>
+323 * <li>xpushing: a boolean that notes whether the object is pushing against something in the x direction.</li>
+324 * <li>ypushing: a boolean that notes whether the object is pushing against something in the y direction.</li>
+325 * <li>haspushing: a boolean that notes if the object changes when pushing against something.</li>
+326 * <li>toucheddown: a boolean that notes if the object is touching something below it on the screen.</li>
+327 * <li>touchedup: a boolean that notes if the object is touching something above it on the screen.<</li>
+328 * <li>touchedright: a boolean that notes if the object is touching something right of it on the screen.<</li>
+329 * <li>touchedleft: a boolean that notes if the object is touching something left of it on the screen.<</li>
+330 * <li>flipside: </li>
+331 * <li>fliph: </li>
+332 * <li>facing: </li>
+333 * <li>frames: </li>
+334 * <li>frame: </li>
+335 * <li>counter: </li>
+336 * </ul>
+337 * // incomplete
+338 */
+339setFrame:function(th){
+340varpref="stand";
+341if(th.xpushing||th.ypushing)
+342if(th.haspushing&&(th.toucheddown||th.touchedup||th.touchedleft||th.touchedright))pref="pushing";elsepref="moving";
+343if(th.flipside)
+344th.fliph=(th.facing==toys.FACE_RIGHT);
+345th.frame=help.decideFrame(th.counter,th.frames[pref+toys.FACES[th.facing]]);
+346},
+347
+348/**
+349 * Checks if the specified object is colliding with tiles in the map in an area defined by the object's colw and colh variables as well as the tolerance and approximation variables that are passed in through data. Only tiles in the map marked as solid are checked against. The alogrithm checks the
+350 * @param {Object} th The object that is being checked against the tilemap.
+351 * @param {Object} map This is the asci map that the tile map is generated from.
+352 * @param {Object} tilemap This is the array of tile objects that it itterated over checking for collisions.
+353 * @param {Object} defaulttile The default tile to be returned if nothing can be found. Null can be used here.
+354 * @param {Object} data Passes is extra dat to the function. Can be set as null.
+355 * <ul>
+356 * <li>tolerance{Integer}: This is subtracted from the collision space to get the maximum collision area for the object. This defaults to 6.</li>
+357 * <li>approximation{Integer}: This is the amount that the checked values are incremented by until they reach the maximum value allowed. This defaults to 10.</li>
+358 * </ul>
+359 */
+360tileCollision:function(th,map,tilemap,defaulttile,data){
+361
+362th.touchedup=false;
+363th.toucheddown=false;
+364th.touchedleft=false;
+365th.touchedright=false;
+366
+367vartolerance=(data&&(data.tolerance!=null)?data.tolerance:6);
+368varapproximation=(data&&(data.approximation!=null)?data.approximation:10);
+369vart=tolerance-approximation;
+370do{
+371t+=approximation;
+372if(t>th.colw-tolerance-1)t=th.colw-tolerance-1;
+373varbottom=help.getTileInMap(th.x+th.colx+t,th.y+th.coly+th.colh-1,map,defaulttile,tilemap);
+374vartop=help.getTileInMap(th.x+th.colx+t,th.y+th.coly,map,defaulttile,tilemap);
+375if(map.tileIsSolid(th,top))th.touchedup=true;
+376if(map.tileIsSolid(th,bottom))th.toucheddown=true;
+377}while(t!=th.colw-tolerance-1);
+378
+379t=tolerance-approximation;
+380do{
+381t+=approximation;
+382if(t>th.colh-tolerance-1)t=th.colh-tolerance-1;
+383varleft=help.getTileInMap(th.x+th.colx,th.y+th.coly+t,map,defaulttile,tilemap);
+384varright=help.getTileInMap(th.x+th.colx+th.colw-1,th.y+th.coly+t,map,defaulttile,tilemap);
+385if(map.tileIsSolid(th,left))th.touchedleft=true;
+386if(map.tileIsSolid(th,right))th.touchedright=true;
+387}while(t!=th.colh-tolerance-1);
+388
+389if(th.touchedup){
+390th.accy=0;
+391th.y=help.yPixelToTile(map,th.y+th.coly,1)-th.coly;
+392}
+393if(th.toucheddown){
+394th.accy=0;
+395th.y=help.yPixelToTile(map,th.y+th.coly+th.colh-1)-th.coly-th.colh;
+396}
+397if(th.touchedleft){
+398th.accx=0;
+399th.x=help.xPixelToTile(map,th.x+th.colx,1)-th.colx;
+400}
+401if(th.touchedright){
+402th.accx=0;
+403th.x=help.xPixelToTile(map,th.x+th.colx+th.colw-1)-th.colx-th.colw;
+404}
+405
+406},
+407
+408/**
+409 * @param {Object} th The object being checked for collisions.
+410 * <ul>
+411 * <li></li>
+412 * <li></li>
+413 * <li></li>
+414 * <li></li>
+415 * </ul>
+416 * @param {Object} data This is used to pass in other data and arguments.
+417 * <ul>
+418 * <li>group {String}: (required) This is the group of objects being checked against.</li>
+419 * <li></li>
+420 * <li></li>
+421 * <li></li>
+422 * <li></li>
+423 * </ul> //incomplete
+424 */
+425spritewallCollision:function(th,data){
+426varwl;
+427for(variingbox._objects[data.group])
+428if((!gbox._objects[data.group][i].initialize)&&toys.topview.collides(th,gbox._objects[data.group][i])){
+429wl=gbox._objects[data.group][i];
+430if(toys.topview.pixelcollides({x:th.x+th.colx,y:th.y+th.coly+th.colhh},wl)){
+431th.touchedleft=true;
+432th.accx=0;
+433th.x=wl.x+wl.colx+wl.colw-th.colx;
+434}elseif(toys.topview.pixelcollides({x:th.x+th.colx+th.colw,y:th.y+th.coly+th.colhh},wl)){
+435th.touchedright=true;
+436th.accx=0;
+437th.x=wl.x+wl.colx-th.colw-th.colx;
+438}
+439if(toys.topview.pixelcollides({x:th.x+th.colx+th.colhw,y:th.y+th.coly+th.colh},wl)){
+440th.toucheddown=true;
+441th.accy=0;
+442th.y=wl.y+wl.coly-th.colh-th.coly;
+443}elseif(toys.topview.pixelcollides({x:th.x+th.colx+th.colhw,y:th.y+th.coly},wl)){
+444th.touchedup=true;
+445th.accy=0;
+446th.y=wl.y+wl.coly+wl.colh-th.coly;
+447}
+448}
+449
+450},
+451
+452/**
+453 * This checks if the object's z index is 0 which means it has hit the floor. If this has occured it also plays an impact or bounce noise if one is passed in. Note: The area above the floor is in the negative z index space so a value of 1 for z will return that the object has collided with the floor and z will then be set to zero.
+454 * @param {Object} th The object being checked for collision.
+455 * <ul>
+456 * <li>touchedfloor{boolean}: This value is not passed in but is created or set in the function. This contains the function's return value.</li>
+457 * <li></li>
+458 * <li></li>
+459 * <li></li>
+460 * </ul>
+461 * @param {Object} data This is used to pass in extra parameters.
+462 * <ul>
+463 * <li></li>
+464 * </ul>
+465 */
+466floorCollision:function(th,data){
+467th.touchedfloor=false;
+468if(th.z>0){
+469th.accz=(data==null?0:-Math.floor(th.accz/data.bounce));
+470if(data&&data.audiobounce&&th.accz)gbox.hitAudio(data.audiobounce);
+471th.z=0;
+472th.touchedfloor=true;
+473}
+474},
+475
+476/**
+477 *
+478 */
+479adjustZindex:function(th){
+480gbox.setZindex(th,th.y+th.h);
+481},
+482
+483/**
+484 *
+485 */
+486// Helper: returns the ahead pixel (i.e. destination use action)
+487getAheadPixel:function(th,data){
+488switch(th.facing){
+489casetoys.FACE_RIGHT:{
+490return{x:th.x+th.colx+th.colw+data.distance,y:th.y+th.coly+th.colhh};
+491break;
+492}
+493casetoys.FACE_LEFT:{
+494return{x:th.x+th.colx-data.distance,y:th.y+th.coly+th.colhh};
+495break;
+496}
+497casetoys.FACE_UP:{
+498return{x:th.x+th.colx+th.colhw,y:th.y+th.coly-data.distance};
+499break;
+500}
+501casetoys.FACE_DOWN:{
+502return{x:th.x+th.colx+th.colhw,y:th.y+th.coly+th.colh+data.distance};
+503break;
+504}
+505}
+506},
+507
+508/**
+509 *
+510 */
+511// Helper: trigger a method in colliding objects (i.e. "use action")
+512callInColliding:function(th,data){
+513for(variingbox._objects[data.group])
+514if((!gbox._objects[data.group][i].initialize)&&toys.topview.pixelcollides(data,gbox._objects[data.group][i]))
+515if(gbox._objects[data.group][i][data.call]){
+516gbox._objects[data.group][i][data.call](th);
+517returni;
+518}
+519returnfalse;
+520},
+521
+522/**
+523 *
+524 */
+525// Enemy methods
+526wander:function(th,map,tilemap,defaulttile,data){
+527if((!th.wandercounter)||(th.toucheddown||th.touchedup||th.touchedleft||th.touchedright)){
+528th.wandercounter=help.random(data.minstep,data.steprange);
+529th.wanderdirection=help.random(0,4);
+530}elseth.wandercounter--;
+531switch(th.wanderdirection){
+532casetoys.FACE_LEFT:{
+533th.xpushing=toys.PUSH_LEFT;
+534th.ypushing=toys.PUSH_NONE;
+535th.facing=toys.FACE_LEFT;
+536th.accx=-data.speed;
+537th.accy=0;
+538break;
+539}
+540casetoys.FACE_RIGHT:{
+541th.xpushing=toys.PUSH_RIGHT;
+542th.ypushing=toys.PUSH_NONE;
+543th.facing=toys.FACE_RIGHT;
+544th.accx=data.speed;
+545th.accy=0;
+546break;
+547}
+548casetoys.FACE_UP:{
+549th.ypushing=toys.PUSH_UP;
+550th.xpushing=toys.PUSH_NONE;
+551th.facing=toys.FACE_UP;
+552th.accy=-data.speed;
+553th.accx=0;
+554break;
+555}
+556casetoys.FACE_DOWN:{
+557th.ypushing=toys.PUSH_DOWN;
+558th.xpushing=toys.PUSH_NONE;
+559th.facing=toys.FACE_DOWN;
+560th.accy=data.speed;
+561th.accx=0;
+562break;
+563}
+564}
+565},
+566
+567/**
+568 *
+569 */
+570// generators (firebullet specific for topdown - more complex than SHMUP one)
+571fireBullet:function(gr,id,data){
+572
+573varts=gbox.getTiles(data.tileset);
+574
+575
+576varobj=gbox.addObject(
+577help.mergeWithModel(
+578data,{
+579_bullet:true,
+580zindex:0,
+581fliph:false,flipv:false,
+582id:id,
+583group:gr,
+584cnt:0,
+585tileset:"",
+586frames:{},
+587acc:0,
+588angle:0,
+589camera:data.from.camera,
+590accx:(data.accx==null?Math.floor(trigo.translateX(0,data.angle,data.acc)):0),
+591accy:(data.accy==null?Math.floor(trigo.translateY(0,data.angle,data.acc)):0),
+592accz:0,
+593x:(data.sidex==toys.FACE_LEFT?data.from.x-ts.tilehw:(data.sidex==toys.FACE_RIGHT?data.from.x+data.from.w-ts.tilehw:data.from.x+data.from.hw-ts.tilehw))+(data.gapx?data.gapx:0),
+594y:(data.sidey==toys.FACE_UP?data.from.y-ts.tilehh:(data.sidey==toys.FACE_DOWN?data.from.y+data.from.h-ts.tilehh:data.from.y+data.from.hh-ts.tilehh))+(data.gapy?data.gapy:0),
+595z:(data.from.z==null?0:data.from.z),
+596collidegroup:"",
+597spark:toys.NOOP,
+598power:1,
+599lifetime:null,
+600tilemap:null,
+601defaulttile:0,
+602applyzgravity:false,
+603map:null,
+604defaulttile:0,
+605mapindex:"",
+606spritewalls:null,
+607colx:(data.fullhit?0:null),
+608coly:(data.fullhit?0:null),
+609colh:(data.fullhit?ts.tileh:null),
+610colw:(data.fullhit?ts.tilew:null),
+611duration:null,
+612onWallHit:function(){
+613this.spark(this);
+614gbox.trashObject(this);
+615},
+616bulletIsAlive:function(){
+617returngbox.objectIsVisible(this);
+618}
+619}
+620)
+621);
+622
+623obj.initialize=function(){
+624toys.topview.initialize(this);
+625};
+626
+627obj[(data.logicon==null?"first":data.logicon)]=function(){
+628this.cnt=(this.cnt+1)%10;
+629
+630if(this.applyzgravity)toys.topview.handleGravity(this);// z-gravity
+631toys.topview.applyForces(this);// Apply forces
+632if(this.applyzgravity)toys.topview.applyGravity(this);// z-gravity
+633if(this.map!=null)toys.topview.tileCollision(this,this.map,this.mapindex,this.defaulttile);// tile collisions
+634if(this.spritewalls!=null)toys.topview.spritewallCollision(this,{group:this.spritewalls});// walls collisions
+635if(this.applyzgravity)toys.topview.floorCollision(this);// Collision with the floor (for z-gravity)
+636toys.topview.adjustZindex(this);
+637if(this.duration!=null){
+638this.duration--;
+639if(this.duration==0)gbox.trashObject(this);
+640}
+641if(!this.bulletIsAlive())gbox.trashObject(this);
+642elseif(this.toucheddown||this.touchedup||this.touchedleft||this.touchedright)this.onWallHit();
+643elseif(this.collidegroup!=null)
+644for(variingbox._objects[this.collidegroup])
+645if((!gbox._objects[this.collidegroup][i].initialize)&&toys.topview.collides(this,gbox._objects[this.collidegroup][i],gbox._objects[this.collidegroup][i].tolerance)){
+646if(gbox._objects[this.collidegroup][i]["hitByBullet"]!=null)
+647if(!gbox._objects[this.collidegroup][i].hitByBullet(this)){
+648this.spark(this);
+649gbox.trashObject(this);
+650}
+651}
+652}
+653
+654obj[(data.bliton==null?"blit":data.bliton)]=function(){
+655if(!gbox.objectIsTrash(this))
+656gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.cnt,this.frames),dx:this.x,dy:this.y+this.z,camera:this.camera,fliph:this.fliph,flipv:this.flipv});
+657}
+658
+659gbox.setZindex(obj,obj.y+obj.h);
+660
+661returnobj;
+662
+663},
+664
+665/**
+666 *
+667 */
+668makedoor:function(gr,id,map,data){
+669
+670varmts=gbox.getTiles(map.tileset);
+671varts=gbox.getTiles(data.tileset);
+672
+673varobj=gbox.addObject(
+674help.mergeWithModel(
+675data,{
+676zindex:0,
+677fliph:false,flipv:false,
+678id:id,
+679group:gr,
+680cnt:0,
+681tileset:"",
+682frames:{},
+683camera:true,
+684x:data.tilex*mts.tilew,
+685y:data.tiley*mts.tileh,
+686z:0,
+687tilemap:null,
+688defaulttile:0,
+689map:map,
+690colx:(data.fullhit?0:null),
+691coly:(data.fullhit?0:null),
+692colh:(data.fullhit?ts.tileh:null),
+693colw:(data.fullhit?ts.tilew:null),
+694opening:false,
+695doorheight:ts.tileh,
+696opencounter:0,
+697opening:false,
+698closing:false,
+699audiobefore:null,
+700audioafter:null,
+701doOpen:function(){
+702this.opening=true;
+703},
+704whenClosed:toys.NOOP,
+705whenOpened:toys.NOOP,
+706whileMoving:toys.NOOP,
+707hitByBullet:function(by){
+708
+709}
+710}
+711)
+712);
+713
+714// Closing animation
+715if(obj.closing)obj.opencounter=obj.doorheight;
+716
+717obj.initialize=function(){
+718this.ismoving=false;
+719toys.topview.initialize(this);
+720};
+721
+722obj[(data.logicon==null?"first":data.logicon)]=function(){
+723if(this.closing){
+724if(!this.ismoving){
+725if(this.audiobefore)gbox.hitAudio(this.audiobefore);
+726this.ismoving=true;
+727}
+728this.whileMoving();
+729this.opencounter--;
+730if(this.opencounter<0){
+731if(this.audioafter)gbox.hitAudio(this.audioafter);
+732this.ismoving=false;
+733this.opencounter=0;
+734this.closing=false;
+735this.whenClosed();
+736}
+737}
+738if(this.opening){
+739if(!this.ismoving){
+740if(this.audiobefore)gbox.hitAudio(this.audiobefore);
+741this.ismoving=true;
+742}
+743this.whileMoving();
+744this.opencounter++;
+745if(this.opencounter>=this.doorheight){
+746if(this.audioafter)gbox.hitAudio(this.audioafter);
+747this.ismoving=false;
+748if(!this.whenOpened())gbox.trashObject(this);
+749}
+750}
+751}
+752
+753obj[(data.bliton==null?"blit":data.bliton)]=function(){
+754if(!gbox.objectIsTrash(this))
+755gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.cnt,this.frames),dx:this.x,dy:this.y+this.z+this.opencounter,h:this.h-this.opencounter,camera:this.camera,fliph:this.fliph,flipv:this.flipv});
+756}
+757
+758gbox.setZindex(obj,obj.y+obj.h);
+759
+760returnobj;
+761},
+762// Set the object speed making sure that the X and Y coords are multiple of the speed. Useful on maze-based games.
+763setStaticSpeed:function(th,speed){
+764th.staticspeed=speed;
+765th.x=Math.round(th.x/speed)*speed;
+766th.y=Math.round(th.y/speed)*speed;
+767}
+768},
+769
+770
+771/**
+772 * @namespace shmup The libraries for a 2D top-down Shmup game.
+773 */
+774// Shoot'em up specifics
+775shmup:{
+776
+777/**
+778 *
+779 */
+780initialize:function(th,data){
+781help.mergeWithModel(
+782th,
+783help.mergeWithModel(
+784data,
+785{
+786x:0,y:0,
+787accx:0,accy:0,
+788frames:{},
+789maxacc:5,controlmaxacc:5,
+790responsive:0,// Responsiveness
+791bounds:{x:0,y:0,w:gbox.getScreenW(),h:gbox.getScreenH()},// Bounds box (ship cannot exit from there)
+792weapon:0,// Weapon
+793hittime:5,
+794camera:false,
+795flipv:false,fliph:false,
+796health:1,
+797tolerance:4
+798}
+799)
+800);
+801toys.shmup.spawn(th);
+802},
+803
+804/**
+805 *
+806 */
+807spawn:function(th,data){
+808th.xpushing=toys.PUSH_NONE;// user is moving side
+809th.vpushing=toys.PUSH_NONE;// user is moving side
+810th.counter=0;// self counter
+811th.hittimer=0;
+812th.killed=false;
+813help.copyModel(th,data);
+814},
+815
+816/**
+817 *
+818 */
+819getNextX:function(th){returnth.x+help.limit(th.accx,-th.maxacc,th.maxacc);},
+820
+821/**
+822 *
+823 */
+824getNextY:function(th){returnth.y+help.limit(th.accy,-th.maxacc,th.maxacc);},
+825
+826/**
+827 *
+828 */
+829controlKeys:function(th,keys){
+830
+831if(gbox.keyIsPressed(keys.left)){
+832th.xpushing=toys.PUSH_LEFT;
+833if(th.accx>th.responsive)th.accx=th.responsive;
+834th.accx=help.limit(th.accx-1,-th.controlmaxacc,th.controlmaxacc);
+835}elseif(gbox.keyIsPressed(keys.right)){
+836th.xpushing=toys.PUSH_RIGHT;
+837if(th.accx<-th.responsive)th.accx=-th.responsive;
+838th.accx=help.limit(th.accx+1,-th.controlmaxacc,th.controlmaxacc);
+839}elseth.xpushing=toys.PUSH_NONE;
+840if(gbox.keyIsPressed(keys.up)){
+841th.ypushing=toys.PUSH_UP;
+842if(th.accy>th.responsive)th.accy=th.responsive;
+843th.accy=help.limit(th.accy-1,-th.controlmaxacc,th.controlmaxacc);
+844}elseif(gbox.keyIsPressed(keys.down)){
+845th.ypushing=toys.PUSH_DOWN;
+846if(th.accy<-th.responsive)th.accy=-th.responsive;
+847th.accy=help.limit(th.accy+1,-th.controlmaxacc,th.controlmaxacc);
+848}elseth.ypushing=toys.PUSH_NONE;
+849},
+850
+851/**
+852 *
+853 */
+854applyForces:function(th){
+855th.x=toys.shmup.getNextX(th);
+856th.y=toys.shmup.getNextY(th);
+857},
+858
+859/**
+860 *
+861 */
+862handleAccellerations:function(th){
+863if(!th.xpushing)th.accx=help.goToZero(th.accx);
+864if(!th.ypushing)th.accy=help.goToZero(th.accy);
+865},
+866
+867/**
+868 *
+869 */
+870keepInBounds:function(th){
+871if(th.x<th.bounds.x){
+872th.x=th.bounds.x;
+873th.accx=0;
+874}elseif(th.x+th.w>th.bounds.x+th.bounds.w){
+875th.x=th.bounds.x+th.bounds.w-th.w;
+876th.accx=0;
+877}
+878if(th.y<th.bounds.y){
+879th.y=th.bounds.y;
+880th.accy=0;
+881}elseif(th.y+th.h>th.bounds.y+th.bounds.h){
+882th.y=th.bounds.y+th.bounds.h-th.h;
+883th.accy=0;
+884}
+885},
+886
+887/**
+888 *
+889 */
+890setFrame:function(th){
+891if(th.hittimer)th.hittimer--;
+892th.frame=help.decideFrame(th.counter,(th.hittimer?th.frames.hit:th.frames.still));
+893},
+894
+895/**
+896 *
+897 */
+898fireBullet:function(gr,id,data){
+899
+900varts=gbox.getTiles(data.tileset);
+901
+902varobj=gbox.addObject(
+903help.mergeWithModel(
+904data,{
+905_bullet:true,
+906fliph:false,flipv:false,
+907id:id,
+908group:gr,
+909cnt:0,
+910tileset:"",
+911frames:{},
+912acc:0,
+913angle:0,
+914camera:false,
+915accx:(data.accx==null?Math.floor(trigo.translateX(0,data.angle,data.acc)):0),
+916accy:(data.accy==null?Math.floor(trigo.translateY(0,data.angle,data.acc)):0),
+917x:data.from.x+data.from.hw-ts.tilehw+(data.gapx?data.gapx:0),
+918y:(data.upper?data.from.y-ts.tilehh+(data.gapy?data.gapy:0):data.from.y+data.from.h-ts.tilehh-(data.gapy?data.gapy:0)),
+919collidegroup:"",
+920spark:toys.NOOP,
+921power:1
+922}
+923)
+924);
+925
+926obj[(data.logicon==null?"first":data.logicon)]=function(){
+927this.x+=this.accx;
+928this.y+=this.accy;
+929this.cnt=(this.cnt+1)%10;
+930if(!gbox.objectIsVisible(this))gbox.trashObject(this);
+931elseif(this.collidegroup!=null)
+932for(variingbox._objects[this.collidegroup])
+933if((!gbox._objects[this.collidegroup][i].initialize)&&gbox.collides(this,gbox._objects[this.collidegroup][i],gbox._objects[this.collidegroup][i].tolerance)){
+934if(gbox._objects[this.collidegroup][i]["hitByBullet"]!=null)
+935if(!gbox._objects[this.collidegroup][i].hitByBullet(this)){
+936this.spark(this);
+937gbox.trashObject(this);
+938}
+939}
+940}
+941
+942obj[(data.bliton==null?"blit":data.bliton)]=function(){
+943gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.cnt,this.frames),dx:this.x,dy:this.y,camera:this.camera,fliph:this.side,flipv:this.flipv});
+944}
+945
+946returnobj;
+947
+948},
+949
+950/**
+951 *
+952 */
+953hitByBullet:function(th,by){
+954if(by.power){
+955th.health-=by.power;
+956if(th.health<=0)th.kill(by);elseth.hittimer=th.hittime;
+957}
+958},
+959
+960/**
+961 *
+962 */
+963generateEnemy:function(gr,id,data,model){
+964help.mergeWithModel(data,model);
+965varobj=gbox.addObject(
+966help.mergeWithModel(
+967data,{
+968id:id,
+969group:gr,
+970cnt:0,
+971tileset:"",
+972frames:{},
+973acc:0,
+974angle:0,
+975camera:false,
+976fliph:false,
+977flipv:false,
+978accx:(data.accx==null?Math.floor(trigo.translateX(0,data.angle,data.acc)):0),
+979accy:(data.accy==null?Math.floor(trigo.translateY(0,data.angle,data.acc)):0),
+980x:data.x,
+981y:data.y,
+982// -- spec
+983animationset:"still",
+984defaultanimationset:"still",
+985hitanimationset:"still",
+986hittime:5,
+987script:toys.NOOP,
+988handler:toys.NOOP,
+989scriptline:(data.scriptline==null?-1:data.scriptline-1),
+990newline:true,
+991waitframes:0,
+992doframes:0,
+993mode:0,
+994line:{},
+995dohandler:null,
+996ended:false,
+997health:1,
+998hittimer:0,
+999kill:toys.NOOP,
+1000tolerance:0,
+1001initialize:null,
+1002invulnerable:false,
+1003hitAnimation:function(time){
+1004this.hittimer=(time==null?this.hittime:time);
+1005this.animationset=this.hitanimationset;
+1006},
+1007goTo:function(nl){// Jump to a line
+1008this.waitframes=0;
+1009this.doframes=0;
+1010this.line={};
+1011this.scriptline=nl-1;
+1012},
+1013hitByBullet:function(by){
+1014if(!this.invulnerable&&by.power){
+1015this.health-=by.power;
+1016if(this.health<=0)this.kill(this,by);elsethis.hitAnimation();
+1017}
+1018},
+1019}
+1020)
+1021);
+1022
+1023
+1024obj[(data.logicon==null?"first":data.logicon)]=function(){
+1025if(this.initialize!=null){
+1026this.initialize(this);
+1027this.initialize=null;
+1028}
+1029if(!this.ended){
+1030if(!this.waitframes&&!this.doframes&&((this.line.waitfor==null)||this.line.waitfor(this))){
+1031this.scriptline++;
+1032this.everycnt=-1;
+1033if(this.script[this.scriptline]==null)
+1034this.ended=true;
+1035else{
+1036if(this.script[this.scriptline].goto!=null)this.scriptline=this.script[this.scriptline].goto;
+1037this.line=this.script[this.scriptline];
+1038if(this.line.afterframes!=null)
+1039this.waitframes=this.line.afterframes;
+1040if(this.line.forframes!=null)
+1041this.doframes=this.line.forframes;
+1042else
+1043this.doframes=1;
+1044if(this.line.cleardo)
+1045this.dohandler=null;
+1046elseif(this.line.doit!=null){
+1047this.dohandler={
+1048actiontimes:0,
+1049timer:(this.line.doit.every=="keep"?this.dohandler.every:this.line.doit.every),
+1050every:(this.line.doit.every=="keep"?this.dohandler.every:this.line.doit.every),
+1051once:(this.line.doit.once=="keep"?this.dohandler.once:this.line.doit.once),
+1052action:(this.line.doit.action=="keep"?this.dohandler.action:this.line.doit.action),
+1053render:(this.line.doit.render=="keep"?this.dohandler.render:this.line.doit.render)
+1054}
+1055}
+1056
+1057}
+1058}
+1059if(!this.waitframes&&this.doframes&&!this.ended){
+1060this.doframes--;
+1061if(this.line.setinvulnerable!=null)this.invulnerable=this.line.setinvulnerable;
+1062if(this.line.setx!=null)this.x=this.line.setx;
+1063if(this.line.sety!=null)this.y=this.line.sety;
+1064if(this.line.addx!=null)this.x+=this.line.addx;
+1065if(this.line.addy!=null)this.y+=this.line.addy;
+1066if(this.line.setaccx!=null)this.accx=this.line.setaccx;
+1067if(this.line.setaccy!=null)this.accy=this.line.setaccy;
+1068if(this.line.setacc!=null){
+1069this.acc=this.line.setacc;
+1070this.accx=Math.floor(trigo.translateX(0,this.angle,this.acc));
+1071this.accy=Math.floor(trigo.translateY(0,this.angle,this.acc));
+1072}
+1073if(this.line.addaccx!=null)this.accx+=this.line.addaccx;
+1074if(this.line.addaccy!=null)this.accy+=this.line.addaccy;
+1075if(this.line.addacc!=null){
+1076this.acc+=this.line.addacc;
+1077this.accx=Math.floor(trigo.translateX(0,this.angle,this.acc));
+1078this.accy=Math.floor(trigo.translateY(0,this.angle,this.acc));
+1079}
+1080
+1081if(this.line.setangle!=null){
+1082this.angle=this.line.setangle;
+1083this.accx=Math.floor(trigo.translateX(0,this.angle,this.acc));
+1084this.accy=Math.floor(trigo.translateY(0,this.angle,this.acc));
+1085}
+1086if(this.line.addangle!=null){
+1087this.angle+=this.line.addangle;
+1088this.accx=Math.floor(trigo.translateX(0,this.angle,this.acc));
+1089this.accy=Math.floor(trigo.translateY(0,this.angle,this.acc));
+1090}
+1091if(this.line.everyframe)this.waitframes=this.line.everyframe;
+1092
+1093}
+1094if(this.waitframes>0)this.waitframes--;
+1095}
+1096if(this.dohandler&&(this.dohandler.action!=null)){
+1097if(this.dohandler.timer==this.dohandler.every){
+1098this.dohandler.action(this,this.dohandler.actiontimes);
+1099this.dohandler.timer=0;
+1100this.dohandler.actiontimes++;
+1101}elseif(!this.dohandler.once)this.dohandler.timer++;
+1102}
+1103if(this.handler!=null)this.handler(this);
+1104
+1105if(this.hittimer){
+1106this.hittimer--;
+1107if(!this.hittimer)this.animationset=this.defaultanimationset;
+1108}
+1109
+1110this.x+=this.accx;
+1111this.y+=this.accy;
+1112this.cnt=(this.cnt+1)%10;
+1113
+1114}
+1115
+1116obj[(data.bliton==null?"blit":data.bliton)]=function(){
+1117gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.cnt,this.frames[this.animationset]),dx:this.x,dy:this.y,camera:this.camera,fliph:this.side,flipv:this.flipv});
+1118if(this.dohandler&&(this.dohandler.render!=null))this.dohandler.render(this);
+1119}
+1120
+1121returnobj;
+1122
+1123},
+1124
+1125/**
+1126 *
+1127 */
+1128generateScroller:function(gr,id,data){
+1129varobj=gbox.addObject(
+1130help.mergeWithModel(
+1131help.cloneObject(data),{
+1132id:id,group:gr,
+1133y:0,x:0,
+1134stage:{},
+1135speed:0,
+1136stop:null,// Remember to set the last stop ever! or the last loop!
+1137block:-1,
+1138bly:0,
+1139lblock:-1,
+1140lbly:0,
+1141lget:0,
+1142tbly:0,
+1143trb:0,
+1144maxwidth:0,
+1145loopstart:null,loopend:null,looprounds:0,
+1146panspeed:1,panstimer:0,destspeed:0,
+1147
+1148setLoop:function(st,en){
+1149this.loopstart=st;
+1150this.loopend=en;
+1151this.lget=1;
+1152this.looprounds=1;
+1153},
+1154
+1155quitLoop:function(){
+1156this.setLoop(null,null);
+1157this.looprounds=0;
+1158},
+1159
+1160setSpeed:function(s){
+1161this.speed=s;
+1162this.destspeed=s;
+1163},
+1164
+1165panToSpeed:function(s,pans){
+1166this.destspeed=s;
+1167this.panspeed=pans;
+1168},
+1169
+1170quitStop:function(){
+1171this.stop=null;
+1172},
+1173
+1174setStop:function(s){
+1175this.stop=s;
+1176},
+1177
+1178setX:function(x){
+1179if(x<0)this.x=0;else
+1180if(x+gbox.getScreenW()>this.maxwidth)this.x=this.maxwidth-gbox.getScreenW();
+1181elsethis.x=x;
+1182}
+1183
+1184}
+1185)
+1186);
+1187
+1188obj[(data.logicon==null?"first":data.logicon)]=function(){
+1189if((this.stop==null)||(this.y<this.stop)){
+1190if(this.speed!=this.destspeed){
+1191if(this.panstimer){
+1192this.panstimer--;
+1193}else{
+1194if(this.speed<this.destspeed)this.speed++;else
+1195if(this.speed>this.destspeed)this.speed--;
+1196this.panstimer=this.panspeed;
+1197}
+1198}
+1199this.y+=this.speed;
+1200if((this.stop!=null)&&(this.y>=this.stop))this.y=this.stop;
+1201if((this.loopend!=null)&&(this.y>this.loopend)){
+1202this.y=this.loopstart+(this.y-this.loopend);
+1203this.looprounds++;
+1204if(this.lget==1){
+1205this.block=0;
+1206this.bly=0;
+1207this.lget=2;
+1208}else{
+1209this.block=this.lblock;
+1210this.bly=this.lbly;
+1211}
+1212
+1213}
+1214}
+1215
+1216// Cerca il blocco da mostrare
+1217this.trb=this.block;
+1218this.tbly=this.bly;
+1219do{
+1220this.trb++;
+1221this.tbly+=gbox.getImage(this.stage[this.trb].image).height;
+1222}while(this.tbly<this.y);
+1223
+1224this.block=this.trb-1;
+1225this.bly=this.tbly-gbox.getImage(this.stage[this.trb].image).height;
+1226
+1227
+1228if(this.lget==2){
+1229this.lblock=this.block;
+1230this.lbly=this.bly;
+1231this.lget=3;
+1232}
+1233
+1234}
+1235
+1236obj[(data.bliton==null?"blit":data.bliton)]=function(){
+1237vardy=this.tbly-this.y;
+1238vardone=false;
+1239do{
+1240if(dy>gbox.getScreenH())done=true;
+1241gbox.blitAll(gbox.getBufferContext(),gbox.getImage(this.stage[this.trb].image),{dx:-this.x,dy:gbox.getScreenH()-dy});
+1242this.trb++;
+1243dy+=gbox.getImage(this.stage[this.trb].image).height;
+1244}while(!done);
+1245}
+1246
+1247returnobj;
+1248}
+1249},
+1250
+1251/**
+1252 * @namespace platformer The libraries for generating a 2D platformer game.
+1253 */
+1254platformer:{
+1255
+1256/**
+1257 *
+1258 */
+1259initialize:function(th,data){
+1260help.mergeWithModel(
+1261th,
+1262help.mergeWithModel(
+1263data,
+1264{
+1265maxaccx:5,maxaccy:10,
+1266jumpsize:6,jumpaccy:6,
+1267accx:0,accy:0,
+1268x:0,y:0,
+1269frames:{},
+1270camera:true,
+1271flipv:false,
+1272side:false
+1273}
+1274)
+1275);
+1276toys.platformer.spawn(th);
+1277},
+1278
+1279/**
+1280 *
+1281 */
+1282spawn:function(th,data){
+1283th.curjsize=0;// current jump size
+1284th.counter=0;// self counter
+1285th.touchedfloor=false;// touched floor
+1286th.touchedceil=false;
+1287th.touchedleftwall=false;
+1288th.touchedrightwall=false;
+1289th.pushing=toys.PUSH_NONE;// user is moving side
+1290th.killed=false;
+1291help.copyModel(th,data);
+1292},
+1293
+1294/**
+1295 *
+1296 */
+1297getNextX:function(th){returnth.x+th.accx;},
+1298
+1299/**
+1300 *
+1301 */
+1302getNextY:function(th){returnth.y+help.limit(th.accy,-th.maxaccy,th.maxaccy);},
+1303
+1304/**
+1305 *
+1306 */
+1307applyGravity:function(th){
+1308th.x=toys.platformer.getNextX(th);
+1309th.y=toys.platformer.getNextY(th);
+1310},
+1311
+1312/**
+1313 *
+1314 */
+1315horizontalKeys:function(th,keys){
+1316if(gbox.keyIsPressed(keys.left)){
+1317th.pushing=toys.PUSH_LEFT;
+1318th.accx=help.limit(th.accx-1,-th.maxaccx,th.maxaccx);
+1319}elseif(gbox.keyIsPressed(keys.right)){
+1320th.pushing=toys.PUSH_RIGHT;
+1321th.accx=help.limit(th.accx+1,-th.maxaccx,th.maxaccx);
+1322}elseth.pushing=toys.PUSH_NONE;
+1323},
+1324
+1325/**
+1326 *
+1327 */
+1328verticalTileCollision:function(th,map,tilemap){
+1329varbottom=help.getTileInMap(th.x+(th.w/2),th.y+th.h,map,0,tilemap);
+1330vartop=help.getTileInMap(th.x+(th.w/2),th.y,map,0,tilemap);
+1331th.touchedfloor=false;
+1332th.touchedceil=false;
+1333
+1334if(map.tileIsSolidCeil(th,top)){
+1335th.accy=0;
+1336th.y=help.yPixelToTile(map,th.y,1);
+1337th.touchedceil=true;
+1338}
+1339if(map.tileIsSolidFloor(th,bottom)){
+1340th.accy=0;
+1341th.y=help.yPixelToTile(map,th.y+th.h)-th.h;
+1342th.touchedfloor=true;
+1343}
+1344},
+1345
+1346/**
+1347 *
+1348 */
+1349horizontalTileCollision:function(th,map,tilemap){
+1350varleft=0;
+1351varright=0;
+1352vart=0;
+1353
+1354th.touchedleftwall=false;
+1355th.touchedrightwall=false;
+1356
+1357while(t<th.h){
+1358left=help.getTileInMap(th.x,th.y+t,map,0,tilemap);
+1359right=help.getTileInMap(th.x+th.w-1,th.y+t,map,0,tilemap);
+1360
+1361if((th.accx<0)&&map.tileIsSolidFloor(th,left)){
+1362th.accx=0;
+1363th.x=help.xPixelToTile(map,th.x-1,1);
+1364th.touchedleftwall=true;
+1365}
+1366if((th.accx>0)&&map.tileIsSolidFloor(th,right)){
+1367th.accx=0;
+1368th.x=help.xPixelToTile(map,th.x+th.w)-th.w;
+1369th.touchedrightwall=true;
+1370}
+1371t+=gbox.getTiles(map.tileset).tileh;
+1372}
+1373},
+1374
+1375/**
+1376 * Checks if the passed object is touching the floor and can therefore jump at present.
+1377 * @param th This is the object being checked for jump ability at the time of calling.
+1378 */
+1379canJump:function(th){
+1380returnth.touchedfloor;
+1381},
+1382
+1383/**
+1384 *
+1385 */
+1386jumpKeys:function(th,key){
+1387if((toys.platformer.canJump(th)||(key.doublejump&&(th.accy>=0)))&&gbox.keyIsHit(key.jump)&&(th.curjsize==0)){
+1388if(key.audiojump)gbox.hitAudio(key.audiojump);
+1389th.accy=-th.jumpaccy;
+1390th.curjsize=th.jumpsize;
+1391returntrue;
+1392}elseif(th.curjsize&&gbox.keyIsHold(key.jump)){// Jump modulation
+1393th.accy--;
+1394th.curjsize--;
+1395}else
+1396th.curjsize=0;
+1397returnfalse;
+1398},
+1399
+1400/**
+1401 *
+1402 */
+1403bounce:function(th,data){
+1404th.curjsize=0;
+1405th.accy=-data.jumpsize;
+1406},
+1407
+1408/**
+1409 *
+1410 */
+1411handleAccellerations:function(th){
+1412// Gravity
+1413if(!th.touchedfloor)th.accy++;
+1414// Attrito
+1415if(th.pushing==toys.PUSH_NONE)if(th.accx)th.accx=help.goToZero(th.accx);
+1416},
+1417
+1418/**
+1419 *
+1420 */
+1421setSide:function(th){
+1422if(th.accx)th.side=th.accx>0;
+1423},
+1424
+1425/**
+1426 *
+1427 */
+1428setFrame:function(th){
+1429if(th.touchedfloor)
+1430if(th.pushing!=toys.PUSH_NONE)
+1431th.frame=help.decideFrame(th.counter,th.frames.walking);
+1432else
+1433th.frame=help.decideFrame(th.counter,th.frames.still);
+1434elseif(th.accy>0)
+1435th.frame=help.decideFrame(th.counter,th.frames.falling);
+1436else
+1437th.frame=help.decideFrame(th.counter,th.frames.jumping);
+1438},
+1439
+1440/**
+1441 *
+1442 */
+1443auto:{
+1444// Moves on a platform. It tries to do not fall down, if specified.
+1445// Args: (object,{moveWhileFalling:<moves while not touching the floor>,speed:<movement speed>})
+1446// Outs: the frame
+1447goomba:function(th,data){
+1448if(data.moveWhileFalling||th.touchedfloor){
+1449if(th.side){
+1450th.pushing=toys.PUSH_LEFT;
+1451th.accx=-data.speed;
+1452}else{
+1453th.pushing=toys.PUSH_RIGHT;
+1454th.accx=data.speed;
+1455}
+1456}elseth.pushing=toys.PUSH_NONE;
+1457},
+1458dontFall:function(th,map,tilemap){
+1459if(th.accx&&th.touchedfloor){
+1460vartil;
+1461if(th.accx>0)til=help.getTileInMap(toys.platformer.getNextX(th)+th.w-1+th.accx,th.y+th.h,map,0,tilemap);
+1462elsetil=help.getTileInMap(toys.platformer.getNextX(th),th.y+th.h,map,0,tilemap);
+1463if(!map.tileIsSolidFloor(th,til)){
+1464th.side=!th.side;
+1465th.accx=0;
+1466}
+1467}
+1468},
+1469horizontalBounce:function(th){
+1470if(th.touchedleftwall||th.touchedrightwall)th.side=!th.side;
+1471},
+1472}
+1473},
+1474
+1475// State-based toys
+1476// CONSTANTS
+1477TOY_BUSY:0,
+1478TOY_DONE:1,
+1479TOY_IDLE:2,
+1480
+1481// PRIVATE
+1482
+1483// Generical toys method
+1484
+1485/**
+1486 *
+1487 */
+1488resetToy:function(th,id){if(th.toys)deleteth.toys[id]},
+1489
+1490/**
+1491 *
+1492 */
+1493getToyValue:function(th,id,v,def){return((th.toys==null)||(th.toys[id]==null)?def:th.toys[id][v])},
+1494
+1495/**
+1496 *
+1497 */
+1498getToyStatus:function(th,id){return((th.toys==null)||(th.toys[id]==null)?toys.TOY_BUSY:th.toys[id].__status)},
+1499
+1500/**
+1501 *
+1502 */
+1503_toydone:function(th,id){
+1504if(th.toys[id].__status<toys.TOY_IDLE)th.toys[id].__status++;
+1505returnth.toys[id].__status;
+1506},
+1507
+1508/**
+1509 *
+1510 */
+1511_toybusy:function(th,id){
+1512th.toys[id].__status=toys.TOY_BUSY;
+1513returnth.toys[id].__status;
+1514},
+1515
+1516/**
+1517 *
+1518 */
+1519_toyfrombool:function(th,id,b){return(b?toys._toydone(th,id):toys._toybusy(th,id))},
+1520
+1521/**
+1522 *
+1523 */
+1524_maketoy:function(th,id){
+1525if(!th.toys)th.toys={};
+1526if(!th.toys[id]){
+1527th.toys[id]={__status:toys.TOY_BUSY};
+1528returntrue;
+1529}elsereturnfalse;
+1530},
+1531
+1532/**
+1533 * @namespace timer Timer functionality based methods
+1534 */
+1535// Pure timers
+1536timer:{
+1537
+1538/**
+1539 *
+1540 */
+1541randomly:function(th,id,data){
+1542if(toys._maketoy(th,id)){
+1543th.toys[id].time=help.random(data.base,data.range);
+1544}
+1545if(th.toys[id].time){
+1546th.toys[id].time--;
+1547returntoys._toybusy(th,id);
+1548}else{
+1549th.toys[id].time=help.random(data.base,data.range);
+1550returntoys._toydone(th,id);
+1551}
+1552},
+1553
+1554/**
+1555 *
+1556 */
+1557real:function(th,id,data){
+1558if(toys._maketoy(th,id)){
+1559th.toys[id].subtimer=gbox.getFps();
+1560th.toys[id].done=false;
+1561if(data.countdown)
+1562th.toys[id].time=data.countdown;
+1563else
+1564th.toys[id].time=0;
+1565}
+1566th.toys[id].subtimer--;
+1567if(!th.toys[id].subtimer){
+1568th.toys[id].subtimer=gbox.getFps();
+1569if(data.countdown){
+1570if(th.toys[id].time){
+1571th.toys[id].time--;
+1572if(data.audiocritical&&(th.toys[id].time<=data.critical))
+1573gbox.hitAudio(data.audiocritical);
+1574}elseth.toys[id].done=true;
+1575}else
+1576th.toys[id].time++;
+1577}
+1578returntoys._toyfrombool(th,id,th.toys[id].done);
+1579
+1580},
+1581
+1582/**
+1583 *
+1584 */
+1585every:function(th,id,frames){
+1586if(toys._maketoy(th,id))th.toys[id].timer=0;
+1587th.toys[id].timer++;
+1588if(th.toys[id].timer==frames){
+1589th.toys[id].timer=0;
+1590returntoys._toydone(th,id);
+1591}elsereturntoys._toybusy(th,id)
+1592},
+1593
+1594/**
+1595 *
+1596 */
+1597after:function(th,id,frames){
+1598if(toys._maketoy(th,id))th.toys[id].timer=0;
+1599if(th.toys[id].timer==frames)returntoys._toydone(th,id);else{
+1600th.toys[id].timer++;
+1601returntoys._toybusy(th,id);
+1602}
+1603}
+1604},
+1605
+1606/**
+1607 *
+1608 */
+1609// Logical helpers
+1610logic:{
+1611
+1612/**
+1613 *
+1614 */
+1615once:function(th,id,cond){
+1616if(toys._maketoy(th,id))th.toys[id].done=false;
+1617if(th.toys[id].done)returnfalse;elseif(cond)th.toys[id].done=true;
+1618returncond;
+1619}
+1620},
+1621
+1622/**
+1623 *
+1624 */
+1625// UI
+1626ui:{
+1627
+1628/**
+1629 *
+1630 */
+1631menu:function(th,id,opt){
+1632if(toys._maketoy(th,id)||opt.resetmenu){
+1633varfd=gbox.getFont(opt.font);
+1634th.toys[id].selected=(opt.selected?opt.selected:0);
+1635th.toys[id].ok=0;
+1636varw=0;
+1637for(vari=0;i<opt.items.length;i++)
+1638if(opt.items[i].length>w)w=opt.items[i].length;
+1639gbox.createCanvas("menu-"+id,{w:w*fd.tilew,h:opt.items.length*fd.tileh});
+1640for(vari=0;i<opt.items.length;i++)
+1641gbox.blitText(gbox.getCanvasContext("menu-"+id),{font:opt.font,text:opt.items[i],dx:0,dy:fd.tileh*i});
+1642th.toys[id].fh=fd.tileh;
+1643th.toys[id].fw=fd.tilew;
+1644}
+1645if(!th.toys[id].ok){
+1646if(gbox.keyIsHit(opt.keys.up)&&(th.toys[id].selected>0)){
+1647if(opt.audiooption)gbox.hitAudio(opt.audiooption);
+1648th.toys[id].selected--;
+1649}else
+1650if(gbox.keyIsHit(opt.keys.down)&&(th.toys[id].selected<opt.items.length-1)){
+1651if(opt.audiooption)gbox.hitAudio(opt.audiooption);
+1652th.toys[id].selected++;
+1653}else
+1654if(gbox.keyIsHit(opt.keys.ok)){
+1655if(opt.audioconfirm)gbox.hitAudio(opt.audioconfirm);
+1656th.toys[id].ok=1;
+1657}else
+1658if(gbox.keyIsHit(opt.keys.cancel))th.toys[id].ok=-1;
+1659}
+1660gbox.blitAll(gbox.getBufferContext(),gbox.getCanvas("menu-"+id),{dx:opt.x+th.toys[id].fw,dy:opt.y,camera:opt.camera});
+1661if(!(th.toys[id].ok%2))gbox.blitText(gbox.getBufferContext(),{font:opt.font,text:opt.selector,dx:opt.x,dy:opt.y+th.toys[id].selected*th.toys[id].fh,camera:opt.camera});
+1662if(th.toys[id].ok){
+1663if(th.toys[id].ok>0)
+1664if(th.toys[id].ok<10){
+1665th.toys[id].ok++;
+1666toys._toybusy(th,id);
+1667}elsereturntoys._toydone(th,id);// selected > 0
+1668elsereturntoys._toydone(th,id);// selected == -1
+1669}elsereturntoys._toybusy(th,id);
+1670},
+1671
+1672/**
+1673 *
+1674 */
+1675// Returns a full customizable object for optimized huds
+1676hud:function(id){
+1677gbox.createCanvas(id);
+1678return{
+1679w:{},
+1680surfaceid:id,
+1681
+1682/**
+1683 *
+1684 */
+1685updateWidget:function(i){
+1686if(!this.w[i].__hidden){
+1687if(this.w[i].widget=="label"){
+1688if(this.w[i].prepad!=null)this.w[i].text=help.prepad(this.w[i].value,this.w[i].prepad,this.w[i].padwith);else
+1689if(this.w[i].postpad!=null)this.w[i].text=help.postpad(this.w[i].value,this.w[i].postpad,this.w[i].padwith);else
+1690this.w[i].text=this.w[i].value+"";
+1691gbox.blitText(gbox.getCanvasContext(this.surfaceid),this.w[i]);
+1692}
+1693if(this.w[i].widget=="symbols"){
+1694varts=gbox.getTiles(this.w[i].tileset);
+1695gbox.blitClear(gbox.getCanvasContext(this.surfaceid),{x:this.w[i].dx,y:this.w[i].dy,w:((this.w[i].maxshown-1)*this.w[i].gapx)+ts.tilew,h:((this.w[i].maxshown-1)*this.w[i].gapy)+ts.tileh});
+1696varcnt=this.w[i].value;
+1697for(varx=0;x<this.w[i].maxshown;x++){
+1698if(cnt>0){
+1699gbox.blitTile(gbox.getCanvasContext(this.surfaceid),{tileset:this.w[i].tileset,tile:this.w[i].tiles[(cnt>this.w[i].tiles.length?this.w[i].tiles.length-1:cnt-1)],dx:this.w[i].dx+(x*this.w[i].gapx),dy:this.w[i].dy+(x*this.w[i].gapy),fliph:this.w[i].fliph,flipv:this.w[i].flipv});
+1700}else
+1701if(this.w[i].emptytile!=null)
+1702gbox.blitTile(gbox.getCanvasContext(this.surfaceid),{tileset:this.w[i].tileset,tile:this.w[i].emptytile,dx:this.w[i].dx+(x*this.w[i].gapx),dy:this.w[i].dy+(x*this.w[i].gapy),fliph:this.w[i].fliph,flipv:this.w[i].flipv});
+1703cnt-=this.w[i].tiles.length;
+1704}
+1705
+1706}
+1707if(this.w[i].widget=="stack"){
+1708varts=gbox.getTiles(this.w[i].tileset);
+1709varbw=((this.w[i].maxshown-1)*this.w[i].gapx)+ts.tilew;
+1710gbox.blitClear(gbox.getCanvasContext(this.surfaceid),{x:this.w[i].dx-(this.w[i].rightalign?bw:0),y:this.w[i].dy,w:bw,h:((this.w[i].maxshown-1)*this.w[i].gapy)+ts.tileh});
+1711for(varx=0;x<this.w[i].maxshown;x++)
+1712if(x<this.w[i].value.length)
+1713gbox.blitTile(gbox.getCanvasContext(this.surfaceid),{tileset:this.w[i].tileset,tile:this.w[i].value[x],dx:(this.w[i].rightalign?this.w[i].dx-ts.tileh-(this.w[i].gapx*x):this.w[i].dx+(x*this.w[i].gapx)),dy:this.w[i].dy+(x*this.w[i].gapy),fliph:this.w[i].fliph,flipv:this.w[i].flipv});
+1714}
+1715if(this.w[i].widget=="radio"){
+1716varts=gbox.getTiles(this.w[i].tileset);
+1717gbox.blitClear(gbox.getCanvasContext(this.surfaceid),{x:this.w[i].dx,y:this.w[i].dy,w:ts.tilew,h:ts.tileh});
+1718gbox.blitTile(gbox.getCanvasContext(this.surfaceid),{tileset:this.w[i].tileset,tile:this.w[i].frames[this.w[i].value],dx:this.w[i].dx,dy:this.w[i].dy,fliph:this.w[i].fliph,flipv:this.w[i].flipv});
+1719
+1720}
+1721if(this.w[i].widget=="blit"){
+1722varts=gbox.getTiles(this.w[i].tileset);
+1723gbox.blitClear(gbox.getCanvasContext(this.surfaceid),{x:this.w[i].dx,y:this.w[i].dy,w:ts.tilew,h:ts.tileh});
+1724gbox.blitTile(gbox.getCanvasContext(this.surfaceid),{tileset:this.w[i].tileset,tile:this.w[i].value,dx:this.w[i].dx,dy:this.w[i].dy,fliph:this.w[i].fliph,flipv:this.w[i].flipv});
+1725
+1726}
+1727if(this.w[i].widget=="bool"){
+1728varts=gbox.getTiles(this.w[i].tileset);
+1729gbox.blitClear(gbox.getCanvasContext(this.surfaceid),{x:this.w[i].dx,y:this.w[i].dy,w:ts.tilew,h:ts.tileh});
+1730if(this.w[i].value)
+1731gbox.blitTile(gbox.getCanvasContext(this.surfaceid),{tileset:this.w[i].tileset,tile:this.w[i].frame,dx:this.w[i].dx,dy:this.w[i].dy,fliph:this.w[i].fliph,flipv:this.w[i].flipv});
+1732}
+1733if(this.w[i].widget=="gauge"){
+1734varts=gbox.getTiles(this.w[i].tileset);
+1735gbox.blitTile(gbox.getCanvasContext(this.surfaceid),{tileset:this.w[i].tileset,tile:0,dx:this.w[i].dx,dy:this.w[i].dy});
+1736gbox.blitTile(gbox.getCanvasContext(this.surfaceid),{tileset:this.w[i].tileset,tile:1,dx:this.w[i].dx,dy:this.w[i].dy,w:(this.w[i].value*ts.tilew)/this.w[i].maxvalue});
+1737}
+1738}
+1739},
+1740
+1741/**
+1742 *
+1743 */
+1744hideWidgets:function(l){
+1745for(vari=0;i<l.length;i++)this.w[l[i]].__hidden=true;
+1746this.redraw();
+1747},
+1748
+1749/**
+1750 *
+1751 */
+1752showWidgets:function(l){
+1753for(vari=0;i<l.length;i++)this.w[l[i]].__hidden=false;
+1754this.redraw();
+1755},
+1756
+1757/**
+1758 *
+1759 */
+1760getValue:function(w,k){
+1761returnthis.w[w][k];
+1762},
+1763
+1764/**
+1765 *
+1766 */
+1767getNumberValue:function(w,k){
+1768returnthis.w[w][k]*1;
+1769},
+1770
+1771/**
+1772 *
+1773 */
+1774setValue:function(w,k,v){
+1775if(this.w[w][k]!=v){
+1776if(k=="value"){
+1777if((this.w[w].maxvalue!=null)&&(v>this.w[w].maxvalue))v=this.w[w].maxvalue;
+1778if((this.w[w].minvalue!=null)&&(v<this.w[w].minvalue))v=this.w[w].minvalue;
+1779if(this.w[w].widget=="radio")v=(v%this.w[w].frames.length);
+1780}
+1781this.w[w][k]=v;
+1782this.updateWidget(w);
+1783}
+1784},
+1785
+1786/**
+1787 *
+1788 */
+1789pushValue:function(w,k,v){
+1790this.w[w][k].push(v);
+1791this.updateWidget(w);
+1792},
+1793
+1794/**
+1795 *
+1796 */
+1797addValue:function(w,k,v){
+1798if(v)this.setValue(w,k,this.w[w][k]+v);
+1799},
+1800
+1801/**
+1802 *
+1803 */
+1804setWidget:function(id,w){
+1805this.w[id]=w;
+1806this.updateWidget(id);
+1807},
+1808
+1809/**
+1810 *
+1811 */
+1812redraw:function(){
+1813gbox.blitClear(gbox.getCanvasContext(this.surfaceid));
+1814for(variinthis.w)this.updateWidget(i);
+1815},
+1816
+1817/**
+1818 *
+1819 */
+1820blit:function(){
+1821gbox.blitAll(gbox.getBufferContext(),gbox.getCanvas(this.surfaceid),{dx:0,dy:0});
+1822}
+1823
+1824}
+1825}
+1826},
+1827
+1828/**
+1829 *
+1830 */
+1831fullscreen:{
+1832
+1833/**
+1834 *
+1835 */
+1836fadeout:function(th,id,tox,data){
+1837if(toys._maketoy(th,id)||data.resetfade){
+1838th.toys[id].fade=0;
+1839if(data.audiofade)th.toys[id].stv=gbox.getAudioVolume(data.audiofade);
+1840if(data.audiochannelfade)th.toys[id].chv=gbox.getChannelVolume(data.audiochannelfade);
+1841}
+1842th.toys[id].fade+=data.fadespeed;
+1843if(th.toys[id].fade>1)th.toys[id].fade=1;
+1844data.alpha=th.toys[id].fade;
+1845gbox.blitFade(tox,data);
+1846if(data.audiofade)gbox.setAudioVolume(data.audiofade,th.toys[id].stv*(1-data.alpha));
+1847if(data.audiochannelfade)
+1848if(data.alpha==1)
+1849gbox.stopChannel(data.audiochannelfade);
+1850else
+1851gbox.setChannelVolume(data.audiochannelfade,th.toys[id].chv*(1-data.alpha));
+1852returntoys._toyfrombool(th,id,th.toys[id].fade==1)
+1853},
+1854
+1855/**
+1856 *
+1857 */
+1858fadein:function(th,id,tox,data){
+1859if(toys._maketoy(th,id)||data.resetfade){
+1860th.toys[id].fade=1;
+1861if(data.audiofade)th.toys[id].stv=gbox.getAudioVolume(data.audiofade);
+1862if(data.audiochannelfade)th.toys[id].chv=gbox.getChannelDefaultVolume(data.audiochannelfade);
+1863}
+1864th.toys[id].fade-=data.fadespeed;
+1865if(th.toys[id].fade<0)th.toys[id].fade=0;
+1866if(th.toys[id].fade){
+1867data.alpha=th.toys[id].fade;
+1868if(data.audiofade)gbox.setAudioVolume(data.audiofade,th.toys[id].stv*(1-data.alpha));
+1869if(data.audiochannelfade)gbox.setChannelVolume(data.audiochannelfade,th.toys[id].chv*(1-data.alpha));
+1870gbox.blitFade(tox,data);
+1871}
+1872returntoys._toyfrombool(th,id,th.toys[id].fade==0);
+1873}
+1874},
+1875
+1876/**
+1877 *
+1878 */
+1879text:{
+1880
+1881/**
+1882 *
+1883 */
+1884blink:function(th,id,tox,data){
+1885if(toys._maketoy(th,id)){
+1886th.toys[id].texttimer=0;
+1887th.toys[id].visible=false;
+1888th.toys[id].times=0;
+1889}
+1890if(th.toys[id].texttimer>=data.blinkspeed){
+1891th.toys[id].texttimer=0;
+1892th.toys[id].visible=!th.toys[id].visible;
+1893if(data.times)th.toys[id].times++;
+1894}elseth.toys[id].texttimer++;
+1895if(th.toys[id].visible)
+1896gbox.blitText(tox,data);
+1897returntoys._toyfrombool(th,id,(data.times?data.times<th.toys[id].times:false));
+1898},
+1899
+1900/**
+1901 *
+1902 */
+1903fixed:function(th,id,tox,data){
+1904if(toys._maketoy(th,id))
+1905th.toys[id].texttimer=0;
+1906else
+1907th.toys[id].texttimer++;
+1908gbox.blitText(tox,data);
+1909returntoys._toyfrombool(th,id,data.time<th.toys[id].texttimer);
+1910}
+1911},
+1912
+1913/**
+1914 *
+1915 */
+1916logos:{
+1917
+1918/**
+1919 *
+1920 */
+1921linear:function(th,id,data){
+1922if(toys._maketoy(th,id)){
+1923th.toys[id].x=data.sx;
+1924th.toys[id].y=data.sy;
+1925th.toys[id].every=data.every;
+1926th.toys[id].played=false;
+1927}
+1928if(!th.toys[id].every){
+1929if((data.x!=th.toys[id].x)||(data.y!=th.toys[id].y))
+1930if((Math.abs(data.x-th.toys[id].x)<=data.speed)&&(Math.abs(data.y-th.toys[id].y)<=data.speed)){
+1931th.toys[id].x=data.x;
+1932th.toys[id].y=data.y;
+1933}else
+1934trigo.translate(th.toys[id],trigo.getAngle(th.toys[id],data),data.speed);
+1935else
+1936if(!th.toys[id].played){
+1937if(data.audioreach)gbox.hitAudio(data.audioreach);
+1938th.toys[id].played=true;
+1939}
+1940th.toys[id].every=data.every;
+1941}elseth.toys[id].every--;
+1942if(data.text)
+1943gbox.blitText(gbox.getBufferContext(),{
+1944font:data.font,
+1945dx:th.toys[id].x,dy:th.toys[id].y,
+1946text:data.text
+1947});
+1948elseif(data.tileset)
+1949gbox.blitTile(gbox.getBufferContext(),{tileset:data.tileset,tile:data.tile,dx:th.toys[id].x,dy:th.toys[id].y,camera:data.camera,fliph:data.fliph,flipv:data.flipv,alpha:data.alpha});
+1950else
+1951gbox.blitAll(gbox.getBufferContext(),gbox.getImage(data.image),{dx:th.toys[id].x,dy:th.toys[id].y,alpha:data.alpha});
+1952returntoys._toyfrombool(th,id,(data.x==th.toys[id].x)&&(data.y==th.toys[id].y));
+1953},
+1954
+1955/**
+1956 *
+1957 */
+1958crossed:function(th,id,data){
+1959if(toys._maketoy(th,id)){
+1960th.toys[id].gapx=data.gapx;
+1961th.toys[id].lw=gbox.getImage(data.image).height;
+1962th.toys[id].done=false;
+1963}
+1964if(th.toys[id].gapx){
+1965th.toys[id].gapx-=data.speed;
+1966if(th.toys[id].gapx<0)th.toys[id].gapx=0;
+1967gbox.blitAll(gbox.getBufferContext(),gbox.getImage(data.image),{dx:data.x-th.toys[id].gapx,dy:data.y,alpha:data.alpha});
+1968gbox.blitAll(gbox.getBufferContext(),gbox.getImage(data.image),{dx:data.x+th.toys[id].gapx,dy:data.y,alpha:data.alpha});
+1969returntoys._toybusy(th,id);
+1970}else{
+1971if(!th.toys[id].done){
+1972th.toys[id].done=true;
+1973if(data.audioreach)gbox.hitAudio(data.audioreach);
+1974}
+1975gbox.blitAll(gbox.getBufferContext(),gbox.getImage(data.image),{dx:data.x,dy:data.y});
+1976returntoys._toydone(th,id);
+1977}
+1978},
+1979
+1980/**
+1981 *
+1982 */
+1983zoomout:function(th,id,data){
+1984if(toys._maketoy(th,id)){
+1985th.toys[id].zoom=data.zoom;
+1986th.toys[id].done=false;
+1987th.toys[id].img=gbox.getImage(data.image);
+1988}
+1989if(th.toys[id].zoom!=1){
+1990th.toys[id].zoom-=data.speed;
+1991if(th.toys[id].zoom<=1){
+1992th.toys[id].zoom=1;
+1993if(data.audioreach)gbox.hitAudio(data.audioreach);
+1994}
+1995gbox.blit(gbox.getBufferContext(),gbox.getImage(data.image),{h:th.toys[id].img.height,w:th.toys[id].img.width,dx:data.x-Math.floor(th.toys[id].img.width*(th.toys[id].zoom-1)/2),dy:data.y-Math.floor(th.toys[id].img.height*(th.toys[id].zoom-1)/2),dh:Math.floor(th.toys[id].img.height*th.toys[id].zoom),dw:Math.floor(th.toys[id].img.width*th.toys[id].zoom),alpha:1/th.toys[id].zoom});
+1996returntoys._toybusy(th,id);
+1997}else{
+1998gbox.blitAll(gbox.getBufferContext(),gbox.getImage(data.image),{dx:data.x,dy:data.y});
+1999returntoys._toydone(th,id);
+2000}
+2001},
+2002
+2003/**
+2004 *
+2005 */
+2006rising:function(th,id,data){
+2007if(toys._maketoy(th,id)){
+2008th.toys[id].cnt=0;
+2009th.toys[id].lh=gbox.getImage(data.image).height;
+2010th.toys[id].lw=gbox.getImage(data.image).width;
+2011th.toys[id].done=false;
+2012}
+2013if(th.toys[id].cnt<th.toys[id].lh){
+2014th.toys[id].cnt+=data.speed;
+2015if(th.toys[id].cnt>th.toys[id].lh)th.toys[id].gapx=th.toys[id].lh;
+2016gbox.blit(gbox.getBufferContext(),gbox.getImage(data.image),{dh:th.toys[id].cnt,dw:th.toys[id].lw,dx:data.x,dy:data.y+th.toys[id].lh-th.toys[id].cnt,alpha:data.alpha});
+2017if(data.reflex)gbox.blit(gbox.getBufferContext(),gbox.getImage(data.image),{dh:th.toys[id].cnt,dw:th.toys[id].lw,dx:data.x,dy:data.y+th.toys[id].lh,alpha:data.reflex,flipv:true});
+2018if(th.toys[id].cnt>=th.toys[id].lh)
+2019if(data.audioreach)gbox.hitAudio(data.audioreach);
+2020returntoys._toybusy(th,id);
+2021}else{
+2022gbox.blitAll(gbox.getBufferContext(),gbox.getImage(data.image),{dx:data.x,dy:data.y});
+2023if(data.reflex)gbox.blitAll(gbox.getBufferContext(),gbox.getImage(data.image),{dx:data.x,dy:data.y+th.toys[id].lh,alpha:data.reflex,flipv:true});
+2024
+2025returntoys._toydone(th,id);
+2026}
+2027},
+2028
+2029/**
+2030 *
+2031 */
+2032bounce:function(th,id,data){
+2033if(toys._maketoy(th,id)){
+2034th.toys[id].accy=data.accy;
+2035th.toys[id].y=data.y;
+2036th.toys[id].h=gbox.getImage(data.image).height;
+2037th.toys[id].done=false;
+2038}
+2039if(!th.toys[id].done){
+2040if(th.toys[id].y+th.toys[id].h>=data.floory){
+2041if(data.audiobounce)gbox.hitAudio(data.audiobounce);
+2042th.toys[id].y=data.floory-th.toys[id].h;
+2043th.toys[id].accy=-Math.ceil(th.toys[id].accy/(data.heavy?data.heavy:2));
+2044th.toys[id].done=(th.toys[id].accy==0);
+2045}elseth.toys[id].accy--;
+2046th.toys[id].y-=th.toys[id].accy;
+2047}
+2048gbox.blitAll(gbox.getBufferContext(),gbox.getImage(data.image),{dx:data.x,dy:th.toys[id].y});
+2049
+2050returntoys._toyfrombool(th,id,th.toys[id].done);
+2051}
+2052},
+2053
+2054/**
+2055 *
+2056 */
+2057dialogue:{
+2058
+2059/**
+2060 *
+2061 */
+2062render:function(th,id,data){
+2063if(toys._maketoy(th,id)){
+2064th.toys[id].newscene=true;
+2065th.toys[id].sceneid=-1;
+2066th.toys[id].ended=false;
+2067th.toys[id].timer=0;
+2068th.toys[id].counter=0;
+2069th.toys[id].anim=0;
+2070gbox.createCanvas("dialogue-"+id);
+2071}
+2072if(!data.hideonend||(data.hideonend&&!th.toys[id].ended)){
+2073if(th.toys[id].newscene&&!th.toys[id].ended){
+2074th.toys[id].anim=0;
+2075th.toys[id].timer=0;
+2076th.toys[id].newscene=false;
+2077th.toys[id].sceneid++;
+2078th.toys[id].ended=(th.toys[id].sceneid==data.scenes.length);
+2079if(!th.toys[id].ended){
+2080th.toys[id].letter=0;
+2081th.toys[id].wait=false;
+2082th.toys[id].scene=data.scenes[th.toys[id].sceneid];
+2083th.toys[id].fd=gbox.getFont((th.toys[id].scene.font?th.toys[id].scene.font:data.font));
+2084th.toys[id].sceneH=(th.toys[id].scene.dh?th.toys[id].scene.dh:gbox.getScreenH());
+2085th.toys[id].sceneW=(th.toys[id].scene.dw?th.toys[id].scene.dw:gbox.getScreenW());
+2086th.toys[id].sceneX=(th.toys[id].scene.dx?th.toys[id].scene.dx:0);
+2087th.toys[id].sceneY=(th.toys[id].scene.dy?th.toys[id].scene.dy:0);
+2088gbox.blitClear(gbox.getCanvasContext("dialogue-"+id));
+2089if(th.toys[id].scene.slide){
+2090gbox.blitAll(gbox.getCanvasContext("dialogue-"+id),gbox.getImage(th.toys[id].scene.slide.image),{dx:th.toys[id].scene.slide.x,dy:th.toys[id].scene.slide.y});
+2091}
+2092if(th.toys[id].scene.scroller){
+2093gbox.createCanvas("scroller-"+id,{w:th.toys[id].sceneW,h:(th.toys[id].scene.scroller.length)*(th.toys[id].fd.tileh+th.toys[id].scene.spacing)});
+2094for(vari=0;i<th.toys[id].scene.scroller.length;i++)
+2095gbox.blitText(gbox.getCanvasContext("scroller-"+id),{
+2096font:th.toys[id].fd.id,
+2097dx:0,
+2098dy:i*(th.toys[id].fd.tileh+th.toys[id].scene.spacing),
+2099dw:th.toys[id].sceneW,
+2100halign:gbox.ALIGN_CENTER,
+2101text:th.toys[id].scene.scroller[i]
+2102});
+2103}
+2104if(th.toys[id].scene.bonus){
+2105gbox.createCanvas("bonus-"+id,{w:th.toys[id].sceneW,h:(th.toys[id].scene.bonus.length)*(th.toys[id].fd.tileh+th.toys[id].scene.spacing)});
+2106}
+2107if(th.toys[id].scene.audiomusic)gbox.hitAudio(th.toys[id].scene.audiomusic);
+2108}
+2109}
+2110if(!th.toys[id].ended){
+2111if(th.toys[id].wait){
+2112if(gbox.keyIsHit(data.esckey))th.toys[id].ended=true;else
+2113if(gbox.keyIsHit(data.skipkey))th.toys[id].newscene=true;
+2114}else{
+2115
+2116// SKIP KEYS
+2117
+2118if(gbox.keyIsHit(data.esckey))th.toys[id].ended=true;else
+2119if(gbox.keyIsHold(data.skipkey))th.toys[id].counter=th.toys[id].scene.speed;
+2120elseth.toys[id].counter++;
+2121
+2122// MOVING
+2123
+2124if(th.toys[id].scene.talk){// DIALOGUES
+2125
+2126
+2127if(th.toys[id].counter==th.toys[id].scene.speed){
+2128th.toys[id].letter++;
+2129th.toys[id].counter=0;
+2130if(th.toys[id].scene.audio&&!(th.toys[id].letter%3))gbox.hitAudio(th.toys[id].scene.audio);
+2131vartmp=th.toys[id].letter;
+2132varrow=0;
+2133while(tmp>th.toys[id].scene.talk[row].length){
+2134tmp-=th.toys[id].scene.talk[row].length;
+2135row++;
+2136if(row==th.toys[id].scene.talk.length){
+2137row=-1;
+2138break;
+2139}
+2140}
+2141if(row>=0){
+2142gbox.blitText(gbox.getCanvasContext("dialogue-"+id),{
+2143font:data.font,
+2144dx:data.who[th.toys[id].scene.who].x,
+2145dy:(data.who[th.toys[id].scene.who].y)+(row*th.toys[id].fd.tileh),
+2146text:th.toys[id].scene.talk[row].substr(0,tmp)
+2147});
+2148}else
+2149th.toys[id].wait=true;
+2150}
+2151
+2152}elseif(th.toys[id].scene.scroller){// SCROLLER (i.e. credits)
+2153
+2154if(th.toys[id].counter==th.toys[id].scene.speed){
+2155th.toys[id].letter++;
+2156th.toys[id].counter=0;
+2157if(th.toys[id].letter==(gbox.getCanvas("scroller-"+id).height+th.toys[id].scene.push))
+2158th.toys[id].wait=true;
+2159}
+2160
+2161}elseif(th.toys[id].scene.bonus){// BONUS (classic bonus award screen)
+2162for(varrow=0;row<=th.toys[id].letter;row++){
+2163if(th.toys[id].scene.bonus[row].text)
+2164gbox.blitText(gbox.getCanvasContext("bonus-"+id),{
+2165font:data.font,
+2166dx:0,
+2167dy:(row*(th.toys[id].fd.tileh+th.toys[id].scene.spacing)),
+2168text:th.toys[id].scene.bonus[row].text
+2169});
+2170elseif(th.toys[id].scene.bonus[row].mul){
+2171// Mask is %VAL%e%MUL%=%TOT%
+2172th.toys[id].scene.bonus[row].tmptext=th.toys[id].scene.bonus[row].mask.replace(/%VAL%/,th.toys[id].timer).replace(/%MUL%/,th.toys[id].scene.bonus[row].mul).replace(/%TOT%/,(th.toys[id].timer*th.toys[id].scene.bonus[row].mul));
+2173gbox.blitText(gbox.getCanvasContext("bonus-"+id),{
+2174clear:true,
+2175font:data.font,
+2176dx:0,
+2177dy:(row*(th.toys[id].fd.tileh+th.toys[id].scene.spacing)),
+2178text:th.toys[id].scene.bonus[row].tmptext
+2179});
+2180}
+2181}
+2182
+2183if(!th.toys[id].wait){
+2184varnext=false;
+2185if(th.toys[id].scene.bonus[th.toys[id].letter].mul&&!th.toys[id].scene.bonus[th.toys[id].letter].text){
+2186if(th.toys[id].counter>=th.toys[id].scene.bonus[th.toys[id].letter].speed){
+2187th.toys[id].counter=0;
+2188th.toys[id].timer++;
+2189if(th.toys[id].timer>th.toys[id].scene.bonus[th.toys[id].letter].mulvalue){
+2190th.toys[id].scene.bonus[th.toys[id].letter].text=th.toys[id].scene.bonus[th.toys[id].letter].tmptext;
+2191next=true;
+2192}else{
+2193if(th.toys[id].scene.bonus[th.toys[id].letter].callback)
+2194th.toys[id].scene.bonus[th.toys[id].letter].callback(th.toys[id].scene.bonus[th.toys[id].letter],th.toys[id].scene.bonus[th.toys[id].letter].arg);
+2195}
+2196}
+2197
+2198}elseif(th.toys[id].counter>=th.toys[id].scene.speed)next=true;
+2199if(next){
+2200if(th.toys[id].letter==th.toys[id].scene.bonus.length-1)
+2201th.toys[id].wait=true;
+2202else{
+2203th.toys[id].letter++;
+2204if(th.toys[id].scene.bonus[th.toys[id].letter].mul){
+2205th.toys[id].scene.bonus[th.toys[id].letter].text=null;
+2206th.toys[id].scene.bonus[th.toys[id].letter].tmptext=null;
+2207th.toys[id].timer=0;
+2208}
+2209th.toys[id].counter=0;
+2210}
+2211}
+2212}
+2213}
+2214
+2215}
+2216
+2217}
+2218
+2219// RENDERING
+2220
+2221
+2222if(th.toys[id].scene.talk){// DIALOGUES
+2223if(data.who[th.toys[id].scene.who].box)
+2224gbox.blitRect(gbox.getBufferContext(),data.who[th.toys[id].scene.who].box);
+2225if(data.who[th.toys[id].scene.who].tileset){
+2226th.toys[id].anim=(th.toys[id].anim+1)%20;
+2227gbox.blitTile(gbox.getBufferContext(),{tileset:data.who[th.toys[id].scene.who].tileset,tile:help.decideFrame(th.toys[id].anim,data.who[th.toys[id].scene.who].frames),dx:data.who[th.toys[id].scene.who].portraitx,dy:data.who[th.toys[id].scene.who].portraity,camera:false,fliph:data.who[th.toys[id].scene.who].fliph,flipv:data.who[th.toys[id].scene.who].flipv});
+2228}
+2229gbox.blitAll(gbox.getBufferContext(),gbox.getCanvas("dialogue-"+id),{dx:0,dy:0});
+2230}elseif(th.toys[id].scene.scroller)// SCROLLER (i.e. credits)
+2231gbox.blit(gbox.getBufferContext(),gbox.getCanvas("scroller-"+id),{dx:th.toys[id].sceneX,dy:th.toys[id].sceneY+(th.toys[id].letter<th.toys[id].sceneH?th.toys[id].sceneH-th.toys[id].letter:0),dw:th.toys[id].sceneW,y:(th.toys[id].letter<th.toys[id].sceneH?0:th.toys[id].letter-th.toys[id].sceneH),dh:(th.toys[id].letter<th.toys[id].sceneH?th.toys[id].letter:th.toys[id].sceneH)});
+2232elseif(th.toys[id].scene.bonus)// BONUS (i.e. credits)
+2233gbox.blitAll(gbox.getBufferContext(),gbox.getCanvas("bonus-"+id),{dx:th.toys[id].sceneX,dy:th.toys[id].sceneY});
+2234}
+2235returntoys._toyfrombool(th,id,th.toys[id].ended);
+2236}
+2237},
+2238
+2239// GENERATORS
+2240
+2241
+2242/**
+2243 *
+2244 */
+2245generate:{
+2246
+2247/**
+2248 *
+2249 */
+2250sparks:{
+2251simple:function(th,group,id,data){
+2252varts=gbox.getTiles(data.tileset);
+2253if(data.frames==null){
+2254data.frames={speed:(data.animspeed==null?1:data.animspeed),frames:[]};
+2255for(vari=0;i<ts.tilerow;i++)data.frames.frames[i]=i;
+2256}
+2257
+2258varobj=gbox.addObject(
+2259help.mergeWithModel(
+2260data,{
+2261id:id,
+2262group:group,
+2263x:th.x+th.hw-ts.tilehw+(data.gapx==null?0:data.gapx),
+2264y:(data.valign=="top"?th.y:th.y+th.hh-ts.tilehh+(data.gapy==null?0:data.gapy)),
+2265tileset:data.tileset,
+2266alpha:null,
+2267accx:0,accy:0,
+2268frame:0,
+2269timer:(data.delay?-data.delay:-1),
+2270frames:data.frames,
+2271toptimer:((data.frames.frames.length)*data.frames.speed)-1,
+2272camera:th.camera,
+2273gravity:false,
+2274trashoffscreen:true,
+2275fliph:(data.fliph==null?th.fliph:data.fliph),flipv:(data.flipv==null?th.flipv:data.flipv),
+2276blinkspeed:0
+2277}
+2278)
+2279);
+2280
+2281obj[(data.logicon==null?"first":data.logicon)]=function(){
+2282this.timer++;
+2283if(this.timer>=0){
+2284this.x+=this.accx;
+2285this.y+=this.accy;
+2286if(this.gravity)this.accy++;
+2287if((this.timer==this.toptimer)||(this.trashoffscreen&&(!gbox.objectIsVisible(this))))gbox.trashObject(this);
+2288}
+2289}
+2290
+2291obj[(data.bliton==null?"blit":data.bliton)]=function(){
+2292if((this.timer>=0)&&(!this.blinkspeed||(Math.floor(this.timer/this.blinkspeed)%2)))
+2293gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.timer,this.frames),dx:this.x,dy:this.y,camera:this.camera,fliph:this.fliph,flipv:this.flipv,alpha:this.alpha});
+2294}
+2295
+2296returnobj;
+2297},
+2298
+2299/**
+2300 *
+2301 */
+2302popupText:function(th,group,id,data){
+2303data.text+="";
+2304varfd=gbox.getFont(data.font);
+2305
+2306varobj=gbox.addObject(
+2307help.mergeWithModel(
+2308data,{
+2309id:id,
+2310group:group,
+2311x:Math.floor(th.x+th.hw-(fd.tilehw*data.text.length)),
+2312y:th.y-fd.tilehh,
+2313vaccy:-data.jump,
+2314font:"small",
+2315keep:0,
+2316text:data.text,
+2317cnt:0,
+2318camera:th.camera
+2319}
+2320)
+2321);
+2322
+2323obj[(data.logicon==null?"first":data.logicon)]=function(){
+2324if(gbox.objectIsVisible(this)){
+2325if(this.vaccy)
+2326this.vaccy++;
+2327else
+2328this.cnt++;
+2329this.y+=this.vaccy;
+2330if(this.cnt>=this.keep)gbox.trashObject(this);
+2331}elsegbox.trashObject(this);
+2332}
+2333
+2334obj[(data.bliton==null?"blit":data.bliton)]=function(){
+2335gbox.blitText(gbox.getBufferContext(),{font:this.font,text:this.text,dx:this.x,dy:this.y,camera:this.camera});
+2336}
+2337
+2338returnobj;
+2339},
+2340
+2341/**
+2342 *
+2343 */
+2344bounceDie:function(th,group,id,data){
+2345varobj=gbox.addObject(
+2346help.mergeWithModel(
+2347data,{
+2348id:id,
+2349group:group,
+2350tileset:th.tileset,
+2351frame:th.frame,
+2352side:th.side,
+2353frames:th.frames.die,
+2354x:th.x,
+2355y:th.y,
+2356vaccy:-data.jump,
+2357accx:0,
+2358flipv:data.flipv,
+2359cnt:0,
+2360blinkspeed:0,
+2361camera:th.camera
+2362}
+2363)
+2364);
+2365
+2366obj[(data.logicon==null?"first":data.logicon)]=function(){
+2367if(gbox.objectIsVisible(this)){
+2368this.vaccy++;
+2369this.y+=this.vaccy;
+2370this.x+=this.accx;
+2371this.cnt++;
+2372}elsegbox.trashObject(this);
+2373}
+2374
+2375obj[(data.bliton==null?"blit":data.bliton)]=function(){
+2376if(!this.blinkspeed||(Math.floor(this.cnt/this.blinkspeed)%2))
+2377gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.cnt,this.frames),dx:this.x,dy:this.y,camera:this.camera,fliph:this.side,flipv:this.flipv});
+2378}
+2379
+2380returnobj;
+2381}
+2382},
+2383
+2384/**
+2385 *
+2386 */
+2387audio:{
+2388
+2389/**
+2390 *
+2391 */
+2392fadeOut:function(th,group,id,data){
+2393varobj=gbox.addObject(
+2394help.mergeWithModel(
+2395data,{
+2396id:id,
+2397group:group,
+2398fadespeed:-0.02*(data.fadein?-1:1),
+2399stoponmute:true,
+2400audio:null,
+2401channel:null,
+2402destination:null
+2403}
+2404)
+2405);
+2406
+2407obj[(data.logicon==null?"first":data.logicon)]=function(){
+2408if(this.destination==null)
+2409if(this.audio)
+2410if(this.fadespeed>0)this.destination=1;elsethis.destination=0;
+2411else
+2412if(this.fadespeed>0)this.destination=gbox.getChannelDefaultVolume(this.channel);elsethis.destination=0;
+2413if(this.fadespeed>0)gbox.playAudio(this.audio);
+2414}
+2415
+2416obj[(data.bliton==null?"blit":data.bliton)]=function(){
+2417if(this.audio)gbox.changeAudioVolume(this.audio,this.fadespeed);
+2418if(this.channel)gbox.changeChannelVolume(this.channel,this.fadespeed);
+2419if(
+2420(this.audio&&(
+2421((this.fadespeed<0)&&(gbox.getAudioVolume(this.audio)<=this.destination))||
+2422((this.fadespeed>0)&&(gbox.getAudioVolume(this.audio)>=this.destination))
+2423))||
+2424(this.channel&&(
+2425((this.fadespeed<0)&&(gbox.getChannelVolume(this.channel)<=this.destination))||
+2426((this.fadespeed>0)&&(gbox.getChannelVolume(this.channel)>=this.destination))
+2427))
+2428){
+2429if(this.channel&&this.stoponmute&&(this.fadespeed<0))gbox.stopChannel(this.channel);
+2430if(this.audio&&this.stoponmute&&(this.fadespeed<0))gbox.stopAudio(this.audio);
+2431gbox.trashObject(this);
+2432}
+2433}
+2434}
+2435
+2436}
+2437
+2438}
+2439
+2440
+2441}
+2442
\ No newline at end of file
diff --git a/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_trigo.js.html b/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_trigo.js.html
new file mode 100644
index 0000000..fdeb4ac
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/akihabara/docs/symbols/src/c__Users_Darius_Desktop_akihabara-1.1_kesiev_akihabara_akihabara_trigo.js.html
@@ -0,0 +1,84 @@
+
1// ---
+ 2// Copyright (c) 2010 Francesco Cottone, http://www.kesiev.com/
+ 3// ---
+ 4
+ 5/**
+ 6 * @namespace Trigo module provides some math stuff for moving objects in a
+ 7 * direction or following a round path.
+ 8 */
+ 9vartrigo={
+ 10ANGLE_RIGHT:0,
+ 11ANGLE_DOWN:Math.PI*0.5,
+ 12ANGLE_LEFT:Math.PI,
+ 13ANGLE_UP:Math.PI*1.5555555,
+ 14
+ 15/**
+ 16 * Adds two angles together (radians).
+ 17 * @param {Float} a Base angle.
+ 18 * @param {Float} add The angle you're adding to the base angle.
+ 19 * @returns The resultant angle, always between 0 and 2*pi.
+ 20 */
+ 21addAngle:function(a,add){
+ 22a=(a+add)%(Math.PI*2);
+ 23if(a<0)return(Math.PI*2)+a;elsereturna;
+ 24},
+ 25/**
+ 26 * Gets the distance between two points.
+ 27 * @param {Object} p1 This is an object containing x and y params for the first point.
+ 28 * @param {Object} p2 This is an object containing x and y params for the second point.
+ 29 * @returns The distance between p1 and p2.
+ 30 */
+ 31getDistance:function(p1,p2){
+ 32returnMath.sqrt(Math.pow(p2.x-p1.x,2)+Math.pow(p2.y-p1.y,2))
+ 33},
+ 34
+ 35/**
+ 36 * Calculates the angle between two points.
+ 37 * @param {Object} p1 This is an object containing x and y params for the first point.
+ 38 * @param {Object} p2 This is an object containing x and y params for the second point.
+ 39 * @param {Float} transl (Optional) Adds an angle (in radians) to the result. Defaults to 0.
+ 40 * @returns The angle between points p1 and p2, plus transl.
+ 41 */
+ 42getAngle:function(p1,p2,transl){
+ 43returnthis.addAngle(Math.atan2(p2.y-p1.y,p2.x-p1.x),(transl?transl:0));
+ 44},
+ 45
+ 46/**
+ 47 * Translates a point by a vector defined by angle and distance. This does not return a value but rather modifies the x and y values of p1.
+ 48 * @param {Object} p1 This is an object containing x and y params for the point.
+ 49 * @param {Float} a The angle of translation (rad).
+ 50 * @param {Float} d The distance of translation.
+ 51 */
+ 52translate:function(p1,a,d){
+ 53p1.x=p1.x+Math.cos(a)*d;
+ 54p1.y=p1.y+Math.sin(a)*d;
+ 55},
+ 56
+ 57/**
+ 58 * Translates an x component of a coordinate by a vector defined by angle and distance. This returns its component translation.
+ 59 * @param {Float} x1 This is an x coordinate.
+ 60 * @param {Float} a The angle of translation (rad).
+ 61 * @param {Float} d The distance of translation.
+ 62 */
+ 63translateX:function(x1,a,d){
+ 64returnx1+Math.cos(a)*d
+ 65},
+ 66
+ 67/**
+ 68 * Translates a y component of a coordinate by a vector defined by angle and distance. This returns its component translation.
+ 69 * @param {Float} y1 This is a y coordinate.
+ 70 * @param {Float} a The angle of translation (rad).
+ 71 * @param {Float} d The distance of translation.
+ 72 */
+ 73translateY:function(y1,a,d){
+ 74returny1+Math.sin(a)*d;
+ 75}
+ 76}
+ 77
\ No newline at end of file
diff --git a/deps/akihabara-core-1.3.1/akihabara/docs/symbols/tool.html b/deps/akihabara-core-1.3.1/akihabara/docs/symbols/tool.html
new file mode 100644
index 0000000..9074480
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/akihabara/docs/symbols/tool.html
@@ -0,0 +1,215 @@
+
+
+
+
+
+
+ JsDoc Reference - tool
+
+
+
+
+
+
+
+
+
+
Tool module provides simple developing tools. Currently, this file only has a cel-composer:
+it can compose an image stacking a set of frames for animating objects, applying a
+number of filters to each frame.
+
+ makecels(data)
+ This makes the image cells for an animation and adds the load event listeners that the other stuff work to them. Calls loadall at the end.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Method Detail
+
+
+
+
+
+
+
+ tool.makecels(data)
+
+
+
+ This makes the image cells for an animation and adds the load event listeners that the other stuff work to them. Calls loadall at the end.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ data
+
+
+ This is the created animation data being passed in to be used by the function.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation generated by JsDoc Toolkit 2.3.2 on Wed Aug 18 2010 16:09:24 GMT-0400 (EDT)
+
Toys module provides lots of common routines during the game developing:
+from effects for screen titles to HUD handling to platform/SHMUP/RPG oriented routines,
+like jumping characters, Z-Indexed objects, bullets, sparks, staff rolls, bonus screens, dialogues etc.
+
+ floorCollision(th, data)
+ This checks if the object's z index is 0 which means it has hit the floor. If this has occured it also plays an impact or bounce noise if one is passed in.
+
+
+ tileCollision(th, map, tilemap, defaulttile, data)
+ Checks if the specified object is colliding with tiles in the map in an area defined by the object's colw and colh variables as well as the tolerance and approximation variables that are passed in through data. Only tiles in the map marked as solid are checked against.
+
+ Sets the objects current location to its next location using the getNextX and getNextY methods.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ th
+
+
+ The object being modified.
+
+
x: the current x position of the object
+
y: the current y position of the object
+
accx: the object's currect acceleration in the x direction
+
accy: the object's currect acceleration in the y direction
+
maxacc: the max accleration the object can have (if either acceleration is greater than this then this value is used instead for that acceleration)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.applyGravity(th)
+
+
+
+ This applies acceleration in the Z direction (not nessesarily gravity but whatever the next accerlation on the Z axis is)
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ th
+
+
+ The object being modified.
+
+
z: the current z position of the object
+
accz: the object's currect acceleration in the z direction
+
maxacc: the max accleration the object can have (if accz is greater than this then this value is used instead)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.callInColliding(th, data)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ th
+
+
+
+
+
+
+
+
+
+
+
+ data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.collides(fr, to, t)
+
+
+
+ Checks if an object checks that both objects are on the same Z plane and if so it calls gbox.collides.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ fr
+
+
+ The object which collision is being checked for.
+
+
x{Integer}: (required)Objects x position
+
y{Integer}: (required)Objects y position
+
z{Integer}: (required)Objects z position
+
colx{Integer}: (required)The dimension of the collision box along the x axis
+
coly{Integer}: (required)The dimension of the collision box along the y axis
+
colh{Integer}: (required)Collision box height
+
colw{Integer}: (required)Collision box width
+
+
+
+
+
+
+ {Object}
+
+
+ to
+
+
+ The object that collision is being checked against.
+
+
x{Integer}: (required)Objects x position
+
y{Integer}: (required)Objects y position
+
z{Integer}: (required)Objects z position
+
colx{Integer}: (required)Collision x
+
coly{Integer}: (required)Collision y
+
colh{Integer}: (required)Collision box height
+
colw{Integer}: (required)Collision box width
+
+
+
+
+
+
+ {int}
+
+
+ t
+
+
+ This is the tollerance (or margin for error) on the collide function.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.controlKeys(th, keys)
+
+
+
+ This sets and runs the control keys for the game.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ th
+
+
+ This is the object that is being controlled by the keys (assumed to be the player)
+
+
accx: the object's currect acceleration in the x direction
+
accy: the object's currect acceleration in the y direction
+
responsive: minimum movement speed
+
staticspeed: turns off acceleration
+
nodiagonals: boolean determining if the object can move along both axis at once.
+
xpushing: a boolean that notes whether the object is pushing against something in the x direction.
+
ypushing: a boolean that notes whether the object is pushing against something in the y direction.
+
controlmaxacc: max acceleration for the object along an axis
+
noreset: checks for the object being allowed to reset its pushing status (?)
+
+
+
+
+
+
+ {Object}
+
+
+ keys
+
+
+ These are the control keys being passed in for left, right, up, and down.
+//incomplete
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.fireBullet(gr, id, data)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ gr
+
+
+
+
+
+
+
+
+
+
+
+ id
+
+
+
+
+
+
+
+
+
+
+
+ data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.floorCollision(th, data)
+
+
+
+ This checks if the object's z index is 0 which means it has hit the floor. If this has occured it also plays an impact or bounce noise if one is passed in. Note: The area above the floor is in the negative z index space so a value of 1 for z will return that the object has collided with the floor and z will then be set to zero.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ th
+
+
+ The object being checked for collision.
+
+
touchedfloor{boolean}: This value is not passed in but is created or set in the function. This contains the function's return value.
+
+
+
+
+
+
+
+
+
+ {Object}
+
+
+ data
+
+
+ This is used to pass in extra parameters.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.getAheadPixel(th, data)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ th
+
+
+
+
+
+
+
+
+
+
+
+ data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.getNextX(th)
+
+
+
+ Gets the next X position the object is going to move to.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ th
+
+
+ The object being checked.
+
+
x: the current x position of the object
+
accx: the object's currect acceleration in the x direction
+
maxacc: the max accleration the object can have (if accx is greater than this then this value is used instead)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.getNextY(th)
+
+
+
+ Gets the next Y position the object is going to move to.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ th
+
+
+ The object being checked.
+
+
y: the current y position of the object
+
accy: the object's currect acceleration in the y direction
+
maxacc: the max accleration the object can have (if accy is greater than this then this value is used instead)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.getNextZ(th)
+
+
+
+ Gets the next Z position the object is going to move to.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ th
+
+
+ The object being checked.
+
+
z: the current z position of the object
+
accz: the object's currect acceleration in the z direction
+
maxacc: the max accleration the object can have (if accz is greater than this then this value is used instead)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.handleAccellerations(th)
+
+
+
+ Degrades all accelerations on an object by one toward zero.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ th
+
+
+ The object being modified.
+
+
xpushing: a boolean that notes whether the object is pushing against something in the x direction.
+
ypushing: a boolean that notes whether the object is pushing against something in the y direction.
+
accx: the object's currect acceleration in the x direction
+
accy: the object's currect acceleration in the y direction
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.handleGravity(th)
+
+
+
+ Increases the Z acceleration on the object by one.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ th
+
+
+ The object being modified.
+
+
accz: the acceleration on the Z axis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.initialize(th, data)
+
+
+
+ Initializes the game with the variables needed for topview and whatever else you feed in through data.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ th
+
+
+ Passes in the object being initialized.
+
+
+
+
+
+ {Object}
+
+
+ data
+
+
+ This is used to pass in everything that's being initiliized. If a value is not in Data then a default value is used instead. This can pass in values which do not have a default.
+
+
x{Integer}: x position of the object. (defaults to 0)
+
y{Integer}: y position of the object. (defaults to 0)
+
z{Integer}: z index of the object. (defaults to 0)
+
accx{Integer}: The starting x velociyt of the object. (defaults to 0)
+
accy{Integer}: The starting y velocity of the object. (defaults to 0)
+
accz{Integer}: The starting z velocity of the object. (defaults to 0)
+
frames{Object}: This is stores the animation frames for the objects in a map style structure. An empty map means the default image will display with no animation frames. (defaults to an empty map)
+
shadow: (defaults to null)
//incomplete
+
maxacc{Integer}: (defaults to )4
+
controlmaxacc{Integer}: (defaults to 4)
+
responsive: (defaults to 0)
+
weapon: (defaults to 0)
+
camera{Boolean}: (defaults to true)
+
flipv{Boolean}: Notes if the object is flipped vertically(defaults to false)
+
fliph{Boolean}: Notes if the object is flipped horrizontally(defaults to false)
+
facing{Integer}: Stores the facing of the object. This is set with pre-defined Integer values from within Toys.(defaults to toys.FACE_DOWN)
+
+
FACE_UP:0,
+
FACE_RIGHT:1,
+
FACE_DOWN:2,
+
FACE_LEFT:3,
+
+
flipside{Boolean}: (defaults to true)
+
haspushing{Boolean}: (defaults to false)
+
frame: (default to 0)
+
colh{Integer}: (defaults to gbox.getTiles(th.tileset).tilehh)
+
colw{Integer}: (defaults to gbox.getTiles(th.tileset).tilew)
+
colx{Integer}: (defaults to 0)
+
staticspeed{Integer}: (defaults to 0)
+
nodiagonals{Boolean}: (defaults to false)
+
noreset: (defaults to false)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.makedoor(gr, id, map, data)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ gr
+
+
+
+
+
+
+
+
+
+
+
+ id
+
+
+
+
+
+
+
+
+
+
+
+ map
+
+
+
+
+
+
+
+
+
+
+
+ data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.pixelcollides(fr, to, t)
+
+
+
+ Checks for pixel collisions with an offset to the X and Y of the colidable using colx and coly.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ fr
+
+
+ The object which collision is being tested for.
+
+
+
+
+
+ {Object}
+
+
+ to
+
+
+ The object (or point) which collision is being tested against.
+
+
+
+
+
+ {int}
+
+
+ t
+
+
+ The tollerance of the collision algorithm.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.setFrame(th)
+
+
+
+ This sets which frame the object is going to display based on an agregate word that describes predefined states.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ th
+
+
+ The object whose frame is being set.
+
+
xpushing: a boolean that notes whether the object is pushing against something in the x direction.
+
ypushing: a boolean that notes whether the object is pushing against something in the y direction.
+
haspushing: a boolean that notes if the object changes when pushing against something.
+
toucheddown: a boolean that notes if the object is touching something below it on the screen.
+
touchedup: a boolean that notes if the object is touching something above it on the screen.<
+
touchedright: a boolean that notes if the object is touching something right of it on the screen.<
+
touchedleft: a boolean that notes if the object is touching something left of it on the screen.<
+
flipside:
+
fliph:
+
facing:
+
frames:
+
frame:
+
counter:
+
+// incomplete
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.setStaticSpeed(th, speed)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+
+
+
+ th
+
+
+
+
+
+
+
+
+
+
+
+ speed
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.spawn(th, data)
+
+
+
+ Spawns a new object in the topview namespace. This also merges parameters in data into paramaters in th using help.copyModel.
+This initializes some basic basic variables for the object and sets the Z index.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ th
+
+
+ References 'this' which is the object that called the method (generally).
+
+
y {Integer}: (required) The object's y position.
+
h {Integer}: (required) The object's height.
+
+
+
+
+
+
+ {Object}
+
+
+ data
+
+
+ This holds variables to be merged into th's stored info.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ toys.topview.spritewallCollision(th, data)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ th
+
+
+ The object being checked for collisions.
+
+
+
+
+
+
+
+
+
+
+
+ {Object}
+
+
+ data
+
+
+ This is used to pass in other data and arguments.
+
+
group {String}: (required) This is the group of objects being checked against.
+ Checks if the specified object is colliding with tiles in the map in an area defined by the object's colw and colh variables as well as the tolerance and approximation variables that are passed in through data. Only tiles in the map marked as solid are checked against. The alogrithm checks the
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ th
+
+
+ The object that is being checked against the tilemap.
+
+
+
+
+
+ {Object}
+
+
+ map
+
+
+ This is the asci map that the tile map is generated from.
+
+
+
+
+
+ {Object}
+
+
+ tilemap
+
+
+ This is the array of tile objects that it itterated over checking for collisions.
+
+
+
+
+
+ {Object}
+
+
+ defaulttile
+
+
+ The default tile to be returned if nothing can be found. Null can be used here.
+
+
+
+
+
+ {Object}
+
+
+ data
+
+
+ Passes is extra dat to the function. Can be set as null.
+
+
tolerance{Integer}: This is subtracted from the collision space to get the maximum collision area for the object. This defaults to 6.
+
approximation{Integer}: This is the amount that the checked values are incremented by until they reach the maximum value allowed. This defaults to 10.
+
+ addAngle(a, add)
+ Adds two angles together (radians).
+
+
+
+
+ getAngle(p1, p2, transl)
+ Calculates the angle between two points.
+
+
+
+
+ getDistance(p1, p2)
+ Gets the distance between two points.
+
+
+
+
+ translate(p1, a, d)
+ Translates a point by a vector defined by angle and distance. This does not return a value but rather modifies the x and y values of p1.
+
+
+
+
+ translateX(x1, a, d)
+ Translates an x component of a coordinate by a vector defined by angle and distance. This returns its component translation.
+
+
+
+
+ translateY(y1, a, d)
+ Translates a y component of a coordinate by a vector defined by angle and distance. This returns its component translation.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Method Detail
+
+
+
+
+
+
+
+ trigo.addAngle(a, add)
+
+
+
+ Adds two angles together (radians).
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Float}
+
+
+ a
+
+
+ Base angle.
+
+
+
+
+
+ {Float}
+
+
+ add
+
+
+ The angle you're adding to the base angle.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ The resultant angle, always between 0 and 2*pi.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ trigo.getAngle(p1, p2, transl)
+
+
+
+ Calculates the angle between two points.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ p1
+
+
+ This is an object containing x and y params for the first point.
+
+
+
+
+
+ {Object}
+
+
+ p2
+
+
+ This is an object containing x and y params for the second point.
+
+
+
+
+
+ {Float}
+
+
+ transl
+
+
+ (Optional) Adds an angle (in radians) to the result. Defaults to 0.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ The angle between points p1 and p2, plus transl.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ trigo.getDistance(p1, p2)
+
+
+
+ Gets the distance between two points.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ p1
+
+
+ This is an object containing x and y params for the first point.
+
+
+
+
+
+ {Object}
+
+
+ p2
+
+
+ This is an object containing x and y params for the second point.
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+ The distance between p1 and p2.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ trigo.translate(p1, a, d)
+
+
+
+ Translates a point by a vector defined by angle and distance. This does not return a value but rather modifies the x and y values of p1.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Object}
+
+
+ p1
+
+
+ This is an object containing x and y params for the point.
+
+
+
+
+
+ {Float}
+
+
+ a
+
+
+ The angle of translation (rad).
+
+
+
+
+
+ {Float}
+
+
+ d
+
+
+ The distance of translation.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ trigo.translateX(x1, a, d)
+
+
+
+ Translates an x component of a coordinate by a vector defined by angle and distance. This returns its component translation.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Float}
+
+
+ x1
+
+
+ This is an x coordinate.
+
+
+
+
+
+ {Float}
+
+
+ a
+
+
+ The angle of translation (rad).
+
+
+
+
+
+ {Float}
+
+
+ d
+
+
+ The distance of translation.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ trigo.translateY(y1, a, d)
+
+
+
+ Translates a y component of a coordinate by a vector defined by angle and distance. This returns its component translation.
+
+
+
+
+
+
+
+
+
+
Parameters:
+
+
+
+
+ {Float}
+
+
+ y1
+
+
+ This is a y coordinate.
+
+
+
+
+
+ {Float}
+
+
+ a
+
+
+ The angle of translation (rad).
+
+
+
+
+
+ {Float}
+
+
+ d
+
+
+ The distance of translation.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation generated by JsDoc Toolkit 2.3.2 on Wed Aug 18 2010 16:09:25 GMT-0400 (EDT)
+
image: reference to the font image loaded (must contain font character tiles in ASCII order)
+ *
firstletter: the ASCII character that the font image's first character corresponds to
+ *
tileh: height in pixels of the character tiles
+ *
tilew: width in pixels of the character tiles
+ *
tilerow: width in pixels of each row in the font image
+ *
gapx: x-coord gap between tile columns, in pixels
+ *
gapy: y-coord gap between tile rows, in pixels
+ * @example
+ * gbox.addImage('font', 'font.png');
+ * gbox.addFont({ id: 'small', image: 'font', firstletter: ' ', tileh: 8, tilew: 8, tilerow: 255, gapx: 0, gapy: 0 });
+ */
+ addFont:function(data) {
+ data.tilehh=Math.floor(data.tileh/2);
+ data.tilehw=Math.floor(data.tilew/2);
+ this._fonts[data.id]=data;
+ this._fonts[data.id].firstascii=data.firstletter.charCodeAt(0);
+ },
+
+ /**
+ * Returns a font object containing data about the font.
+ * @param {String} id The id of the font, as set in gbox.addFont.
+ */
+ getFont:function(id) {
+ return this._fonts[id];
+ },
+
+ /**
+ * Deletes an object, keeping a record of its group and id in gbox._garbage.
+ * @param {Object} obj The object you wish to delete.
+ */
+ trashObject:function(obj) {
+ if (!this._garbage[obj.group]) this._garbage[obj.group]={};
+ this._garbage[obj.group][obj.id]=1;
+ obj.__trashing=true;
+ },
+
+ /**
+ * Clears the record held in gbox._garbage of what has been deleted. The "onpurge" method is called on the object before being deleted (for canvas deallocation etc.)
+ */
+ purgeGarbage:function() {
+ for (var group in this._garbage)
+ for (var id in this._garbage[group]) {
+ if (this._objects[group][id]["onpurge"]) this._objects[group][id].onpurge();
+ if (this._objects[group][id].__zt!=null)
+ this._zindex.remove(this._objects[group][id].__zt)
+ delete this._objects[group][id];
+ }
+ gbox._garbage={};
+ },
+
+ /**
+ * Deletes every object in a given group.
+ * @param {String} group The group id.
+ */
+ trashGroup:function(group) {
+ if (!this._garbage[group]) this._garbage[group]={};
+ for (var obj in this._objects[group])
+ this._garbage[group][obj]=1;
+ },
+
+ /**
+ * Returns whether an object is due to be trashed. Useful in cases you want to check if
+ * an object is marked as trash before it is actually deleted.
+ * @param {Object} o The object you're checking.
+ * @returns {Boolean} True if the object is marked as trash.
+ */
+ objectIsTrash:function(o) { return o.__trashing },
+
+ /**
+ * Creates a new game object. Generally speaking you pass a fully-defined object as the parameter (including a group, id, tileset, and so on).
+ * A group must be specified, or the program will crash. If no id is specified, then it will automatically provide
+ * an id of 'obj-XXXX' where 'XXXX' is an automatically incrementing integer. This is where the initialize, first, and blit
+ * functions are defined, as well.
+ * @param {Object} data The object you wish to create.
+ * @returns {Object} The object you created.
+ * @example
+ * data = {
+ * group: 'player',
+ * id: 'player_id',
+ * tileset: 'player_tiles',
+ * x: 0,
+ * y: 0,
+ * initialize: function() {
+ this.x = 10;
+ this.y = 10;
+ },
+ * };
+ * gbox.addObject(data);
+ */
+ addObject:function(data) {
+ // Extras
+ if (!data.id) {
+ data.id="obj-"+this._autoid;
+ this._autoid=(this._autoid+1)%1000;
+ }
+ if (data.tileset) {
+ if (data.h==null) data.h=this._tiles[data.tileset].tileh;
+ if (data.w==null) data.w=this._tiles[data.tileset].tilew;
+ if (data.hw==null) data.hw=this._tiles[data.tileset].tilehw;
+ if (data.hh==null) data.hh=this._tiles[data.tileset].tilehh;
+ }
+ this._objects[data.group][data.id]=data;
+ if (data.zindex!=null)
+ this.setZindex(this._objects[data.group][data.id],data.zindex);
+ return this._objects[data.group][data.id];
+ },
+
+ /**
+ * Returns whether a given group contains no objets.
+ * @param {String} gid The group you're checking.
+ * @returns {Boolean} True if the group contains no objects.
+ */
+ groupIsEmpty:function(gid) { for (var i in this._objects[gid]) return false; return true; },
+
+ /**
+ * Creates a new canvas. By default, the width and height is the current gbox._screenw and gbox._screenh,
+ * but it can also be set by passing in a data object with the appropriate parameters.
+ * @param {String} id The id of the new canvas.
+ * @param {Object} data (Optional) The height and width of the new canvas, contained in data.h and data.w parameters.
+ * @example
+ * gbox.createCanvas('newCanvas', {w: 640, h: 480});
+ */
+ createCanvas:function(id,data) {
+ this.deleteCanvas(id);
+ var w=(data&&data.w?data.w:this._screenw);
+ var h=(data&&data.h?data.h:this._screenh);
+ this._canvas[id]=document.createElement("canvas");
+ this._canvas[id].setAttribute('height',h);
+ this._canvas[id].setAttribute('width',w);
+ this._canvas[id].getContext("2d").save();
+ this._canvas[id].getContext("2d").globalAlpha=0;
+ this._canvas[id].getContext("2d").fillStyle = gbox.COLOR_BLACK;
+ this._canvas[id].getContext("2d").fillRect(0,0,w,h);
+ this._canvas[id].getContext("2d").restore();
+ },
+ /**
+ * Swap two canvas using their ID.
+ * @param {String} id The id of the first canvas.
+ * @param {String} id The id of the second canvas.
+ * @example
+ * gbox.swapCanvas('canvas1','canvas2');
+ */
+ swapCanvas:function(a,b) {
+ var swp=this._canvas[a];
+ this._canvas[a]=this._canvas[b];
+ this._canvas[b]=swp;
+ },
+ /**
+ * Deletes a given canvas.
+ * @param {String} id The id of the canvas to be deleted.
+ */
+ deleteCanvas:function(id) {
+ if (this._canvas[id]) delete this._canvas[id];
+ },
+
+ /**
+ * Checks to see if an image was successfully loaded.
+ * @param {String} id The id of the image.
+ * @returns {Boolean} True if the image has been loaded.
+ */
+ imageIsLoaded:function(id){ return this._images[id]&&(this._images[id].getAttribute("wasloaded"))&&this._images[id].width },
+
+ /**
+ * Gets information about a loaded image.
+ * @param {String} id The id of the image.
+ * @returns {Object} A DOM Image element, including the URL and last modified date of the image, its ID, and whether it was loaded successfully.
+ * @example
+ * image = gbox.getImage('logo');
+ * image; // => ?
+ */
+ getImage:function(id){return this._images[id]},
+
+ /**
+ * Gets the buffer canvas (automatically created by gbox.initScreen).
+ * @returns {Object} A DOM Canvas element, including the width and height of the canvas.
+ */
+ getBuffer:function(){return (gbox._fskid>=gbox._frameskip?(this._db?this.getCanvas("_buffer"):this._screen):null)},
+
+ /**
+ * Gets the buffer canvas context.
+ * @returns {Object} A DOM Canvas context object.
+ */
+ getBufferContext:function(){ return (gbox._fskid>=gbox._frameskip?(this._db?this.getCanvasContext("_buffer"):this._screen.getContext("2d")):null) },
+
+ /**
+ * Gets a given canvas.
+ * @param {Object} id The identifier of the canvas.
+ * @returns {Object} A DOM Canvas element, including the width and height of the canvas.
+ */
+ getCanvas:function(id){return this._canvas[id]},
+
+ /**
+ * Gets the two-dimensional canvas context of a given canvas. The object it returns contains all the drawing functions for the canvas.
+ * See W3C and
+ * Mozilla Developer Center for details.
+ * @param {Object} id The identifier of the canvas.
+ * @returns {Object} A DOM Canvas context object.
+ */
+ getCanvasContext:function(id){return this.getCanvas(id).getContext("2d");},
+
+ /**
+ * Adds an image file to the loader, assigning it to an ID. If adding an image to an existing ID, it checks to see if the file you're
+ * adding is different than the one currently assigned to the ID. If it's different, it overwrites the old image. If it's the same, then
+ * no action is taken.
+ * @param {String} id The identifier of the image.
+ * @param {String} filename The file name of the image.
+ */
+ addImage:function(id,filename) {
+ if (this._images[id])
+ if (this._images[id].getAttribute("src_org")==filename)
+ return;
+ else
+ delete this._images[id];
+ this._addtoloader({type:"image",id:id,filename:filename});
+ },
+
+ /**
+ * Deletes an image currently in use. Does not delete the image file, but removes it from Akihabara's image list.
+ * @param {String} id The identifier of the image.
+ */
+ deleteImage:function(id) {
+ delete this._images[id];
+ },
+
+ /**
+ * Creates a new Akihabara tileset, adding it to the engine.
+ * @param {Object} t An object containing:
id {String}: the new id of the tileset
+ *
image {String}: reference to the tileset image loaded
+ *
tileh {Integer}: height in pixels of the tiles
+ *
tilew {Integer}: width in pixels of the tiles
+ *
tilerow {Integer}: width in pixels of each row in the font image
+ *
gapx {Integer}: x-coord gap between tile columns, in pixels
+ *
gapy {Integer}: y-coord gap between tile rows, in pixels
+ */
+ addTiles:function(t) {
+ t.tilehh=Math.floor(t.tileh/2);
+ t.tilehw=Math.floor(t.tilew/2);
+ this._tiles[t.id]=t;
+ },
+
+ /**
+ * Gets an Akihabara tileset, adding it to the engine.
+ * @param {String} t The ID of a tileset.
+ * @returns An object containing:
id {String}: the new id of the tileset
+ *
image {String}: reference to the tileset image loaded
+ *
tileh {Integer}: height in pixels of the tiles
+ *
tilew {Integer}: width in pixels of the tiles
+ *
tilerow {Integer}: width in pixels of each row in the font image
+ *
gapx {Integer}: x-coord gap between tile columns, in pixels
+ *
gapy {Integer}: y-coord gap between tile rows, in pixels
+ */
+ getTiles:function(t) { return this._tiles[t] },
+
+ /**
+ * Loads the initial splash screen and debugging font, then calls gbox._waitforloaded which adds to the game all the previously
+ * defined resources. Once gbox._waitforloaded is done, it calls the callback function cb.
+ * @params {String} cb The name of the function to be called when all assets are done loading.
+ */
+ loadAll:function(cb) {
+ // Setup logger
+ if (this._canlog) this.log=console.log;
+ // Set the callback function, which is called after the resources are loaded.
+ if (!this._cb) this._cb = cb;
+ // Default stuff
+ this.addImage("_dbf","akihabara/debugfont.png");
+ if (this._splash.background) this.addImage("_splash",this._splash.background);
+ gbox.addFont({id:"_dbf",image:"_dbf",firstletter:" ",tileh:5,tilew:4,tilerow:16,gapx:0,gapy:0});
+ if (!gbox._splash.minimalTime)
+ gbox._minimalexpired=2;
+ this._waitforloaded();
+ },
+
+ _implicitsargs:function(data) {
+ if (data.camera) {
+ data.dx-=this._camera.x;
+ data.dy-=this._camera.y;
+ }
+ if (data.sourcecamera) {
+ data.x=this._camera.x*(data.parallaxx?data.parallaxx:1);
+ data.y=this._camera.y*(data.parallaxy?data.parallaxy:1);
+ }
+ },
+
+ /**
+ * Draws a tile to a canvas context
+ * @param {Object} tox The canvas context to be drawn on.
+ * @param {Object} data An object containing data about the tile to be drawn, including:
+ *
tileset {String}: the id of the tileset
+ *
tile {Integer}: the index of the tile within the tileset to be drawn
+ *
dx {Integer}: x coordinate to draw the tile at
+ *
dy {Integer}: y coordinate to draw the tile at
+ *
fliph {Integer}: horizontal flip, either 1 or -1
+ *
flipv {Integer}: vertical flip, either 1 or -1
+ *
alpha {Float}: alpha value (0 is transparent, 1 is opaque)
+ * @example
+ * // from capman, draws an current object's tile, called from inside its blit function
+ * gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:this.frame,dx:this.x,dy:this.y,fliph:this.fliph,flipv:this.flipv,camera:this.camera,alpha:1});
+ */
+ blitTile:function(tox,data) {
+ if (tox==null) return;
+ var ts=this._tiles[data.tileset];
+ var img=this.getImage(ts.image);
+ this._implicitsargs(data);
+ tox.save();
+ tox.globalAlpha=(data.alpha?data.alpha:1);
+ tox.translate((data.fliph?ts.tilew:0), (data.flipv?ts.tileh:0)); tox.scale((data.fliph?-1:1), (data.flipv?-1:1));
+ this._safedrawimage(tox,img, ts.gapx+(ts.tilew*(data.tile%ts.tilerow)),ts.gapy+(ts.tileh*Math.floor(data.tile/ts.tilerow)),(data.w==null?ts.tilew:data.w),(data.h==null?ts.tileh:data.h),data.dx*(data.fliph?-1:1),data.dy*(data.flipv?-1:1),(data.w?data.w:ts.tilew),(data.h?data.h:ts.tileh));
+ tox.restore();
+ },
+
+ /**
+ * Draws an image to a canvas context
+ * @param {Object} tox The canvas context to be drawn on.
+ * @param {Object} image The image to draw. Must be a DOM Image element, typicallly accessed via gbox.getImage
+ * @param {Object} data An object containing data about the tile to be drawn, including:
+ *
dx {Integer}: (required) x coordinate to draw the image at
+ *
dy {Integer}: (required) y coordinate to draw the image at
+ *
fliph {Integer}: horizontal flip, either 1 or -1
+ *
flipv {Integer}: vertical flip, either 1 or -1
+ *
alpha {Float}: alpha value (0 is transparent, 1 is opaque)
+ * @example
+ * // draw an image at (100,100)
+ * gbox.blitAll(gbox.getBufferContext(),gbox.getImage("image_id"),{dx:100,dy:100});
+ */
+ blitAll:function(tox,image,data) {
+ if (tox==null) return;
+ this._implicitsargs(data);
+ tox.save();
+ tox.globalAlpha=(data.alpha?data.alpha:1);
+ tox.translate((data.fliph?image.width:0), (data.flipv?image.height:0)); tox.scale((data.fliph?-1:1), (data.flipv?-1:1));
+ this._safedrawimage(tox,image, 0,0, image.width,image.height,data.dx*(data.fliph?-1:1),data.dy*(data.flipv?-1:1),image.width,image.height);
+ tox.restore();
+ },
+
+ blit:function(tox,image,data) {
+ if (tox==null) return;
+ this._implicitsargs(data);
+ tox.save();
+ tox.globalAlpha=(data.alpha?data.alpha:1);
+ tox.translate((data.fliph?data.dw:0), (data.flipv?data.dh:0)); tox.scale((data.fliph?-1:1), (data.flipv?-1:1));
+ this._safedrawimage(tox,image,(data.x?data.x:0), (data.y?data.y:0),(data.w?data.w:data.dw),(data.h?data.h:data.dh),data.dx*(data.fliph?-1:1),data.dy*(data.flipv?-1:1),data.dw,data.dh);
+ tox.restore();
+ },
+
+
+ /**
+ * Draws a tilemap to a canvas context
+ * @param {Object} tox The canvas context to be drawn on.
+ * @param {Object} data An object containing a set of tilemap data, including:
+ *
tileset {String}: (required) the id of the tileset the tilemap is based on
+ *
map {Array}: an array whose x and y coord represent the tilemap coordinates, containing integers that correspond to the index of a given tile (or null for no tile)
+ */
+ blitTilemap:function(tox,data) {
+ if (tox==null) return;
+ var ts=this._tiles[data.tileset];
+ for (var y=0;y
font {String}: (required) the id of font to draw the text with
+ *
text {String}: (required) the text to display
+ *
dx {Integer}: (required) the x coordinate to draw the text at
+ *
dy {Integer}: (required) the y coordinate to draw the text at
+ *
dw {Integer}: the width of the text area -- required if you define data.halign
+ *
dh {Integer}: the height of the text area -- required if you define data.valign
+ *
valign {Integer}: either gbox.ALIGN_BOTTOM (aligns from the bottom of the text area) or gbox.ALIGN_MIDDLE (vertically centers text in text area)
+ *
halign {Integer}: either gbox.ALIGN_RIGHT (aligns to the right hand side of text area) or gbox.ALIGN_CENTER (horizontallly centers text in text area)
+ *
alpha {Float}: alpha value (0 is transparent, 1 is opaque)
+ */
+ blitText:function(tox,data) {
+ if (tox==null) return;
+ data.text+=""; // Convert to string.
+ var fn=this._fonts[data.font];
+ var tile=0;
+ this._implicitsargs(data);
+ var dx=data.dx;
+ var dy=data.dy;
+ if (data.valign==gbox.ALIGN_BOTTOM) dy = dy+data.dh-fn.tileh;
+ else if (data.valign==gbox.ALIGN_MIDDLE) dy = dy+Math.floor(data.dh/2)-fn.tileh;
+ if (data.halign==gbox.ALIGN_RIGHT) dx = dx+data.dw-(data.text.length*fn.tilew);
+ else if (data.halign==gbox.ALIGN_CENTER) dx = dx+Math.floor((data.dw-(data.text.length*fn.tilew))/2);
+ tox.save();
+ tox.globalAlpha=(data.alpha?data.alpha:1);
+ for (var y=0;y=0) {
+ if (data.clear) tox.clearRect(dx+(y*fn.tilew),dy,(data.w?data.w:fn.tilew),(data.h?data.h:fn.tileh));
+ this._safedrawimage(tox,this.getImage(fn.image), fn.gapx+(fn.tilew*(tile%fn.tilerow)),
+ fn.gapy+(fn.tileh*Math.floor(tile/fn.tilerow)),fn.tilew,fn.tileh,dx+(y*fn.tilew),dy,(data.w?data.w:fn.tilew),(data.h?data.h:fn.tileh));
+ }
+ }
+ tox.restore();
+ },
+
+ /**
+ * Clears a rectangular area of a canvas context.
+ * @param {Object} image The canvas context to be drawn on.
+ * @param {Object} data An object containing a set of data, including:
+ *
x {Integer}: (required) the x coordinate of the top-left corner of the rectangle
+ *
y {Integer}: (required) the y coordinate of the top-left corner of the rectangle
+ *
w {Integer}: the width of the box; defaults to canvas width
+ *
h {Integer}: the height the box; defaults to canvas height
+ */
+ blitClear:function(image,data) {
+ if (image==null) return;
+ if (data==null) data={x:0,y:0};
+ this._implicitsargs(data);
+ image.clearRect(data.x,data.y,(data.w==null?image.canvas.width:data.w),(data.h==null?image.canvas.height:data.h));
+ },
+
+ /**
+ * Draws an image directly to the screen's current canvas context. Used internally in gbox.go(). Probably shouldn't be used otherwise.
+ */
+ blitImageToScreen:function(image) {
+ this._screen.getContext("2d").drawImage(image,0,0);
+ },
+
+ /**
+ * Draws a filled rectangle over an entire canvas context.
+ * @param {Object} tox The canvas context to be filled.
+ * @param {Object} data An object containing a set of data, including:
+ *
alpha {Float}: the alpha value of the rectangle; defaults to 1
+ *
color {Object}: the color of the box, formatted rgb(rValue, gValue, bValue); default black
+ */
+ blitFade:function(tox,data) {
+ if (tox) this.blitRect(tox,{x:0,y:0,w:tox.canvas.width,h:tox.canvas.height,alpha:data.alpha,color:data.color});
+ },
+
+ /**
+ * Draws a filled rectangle to a canvas context.
+ * @param {Object} tox The canvas context to be drawn on.
+ * @param {Object} data An object containing a set of data, including:
+ *
x {Integer}: (required) the x coordinate of the top-left corner of the rectangle
+ *
y {Integer}: (required) the y coordinate of the top-left corner of the rectangle
+ *
w {Integer}: (required) the width of the box
+ *
h {Integer}: (required) the height the box
+ *
alpha {Float}: the alpha value of the rectangle; defaults to 1
+ *
color {Object}: the color of the box, formatted rgb(rValue, gValue, bValue); default black
+ */
+ blitRect:function(tox,data) {
+ if (tox==null) return;
+ tox.save();
+ tox.globalAlpha=(data.alpha?data.alpha:1);
+ tox.fillStyle = (data.color?data.color:gbox.COLOR_BLACK);
+ tox.fillRect(data.x,data.y,data.w,data.h);
+ tox.restore();
+ },
+
+ /**
+ * Calculates a box collision between two collision boxes within a given tolerance. A higher tolerance means less precise collision.
+ * @param {Object} o1 A collision box you're testing for collision. Must contain:
+ *
x {Integer}: (required) the x coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
+ *
y {Integer}: (required) the y coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
+ *
w {Integer}: (required) the width of the box
+ *
h {Integer}: (required) the height the box
+ * @param {Object} o2 A collision box you're testing for collision. Must contain:
+ *
x {Integer}: (required) the x coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
+ *
y {Integer}: (required) the y coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
+ *
w {Integer}: (required) the width of the box
+ *
h {Integer}: (required) the height the box
+ * @param {Integer} t The tolerance for the collision, in pixels. A value of 0 means pixel-perfect box collision. A value of 2 would mean that the
+ * boxes could overlap by up to 2 pixels without being considered a collision.
+ * @returns True if the two collision boxes are colliding within the given tolerance.
+ */
+ collides:function(o1,o2,t) {
+ if (!t) t=0;
+ return !((o1.y+o1.h-1-t o2.y+o2.h-1-t) || (o1.x+o1.w-1-to2.x+o2.w-1-t));
+ },
+
+ /**
+ * Calculates a point-box collision between a point and a collision box within a given tolerance. A higher tolerance means less precise collision.
+ * @param {Object} o1 A point you're testing for collision. Must contain:
+ *
x {Integer}: (required) the x coordinate of the point
+ *
y {Integer}: (required) the y coordinate of the point
+ * @param {Object} o2 A collision box you're testing for collision. Must contain:
+ *
x {Integer}: (required) the x coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
+ *
y {Integer}: (required) the y coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
+ *
w {Integer}: (required) the width of the box
+ *
h {Integer}: (required) the height the box
+ * @param {Integer} t The tolerance for the collision, in pixels. A value of 0 means pixel-perfect collision. A value of 2 would mean that the
+ * point could exist within the outermost 2 pixels of the box without being considered a collision.
+ * @returns True if the point is colliding with the box within the given tolerance.
+ */
+ pixelcollides:function(o1,o2,t) {
+ if (!t) t=0;
+ return !((o1.y o2.y+o2.h-1-t) || (o1.xo2.x+o2.w-1-t));
+ },
+
+ /**
+ * Determines whether an object is visible by seeing if it collides with the camera's viewport.
+ * @param {Object} obj The object you're testing to see if it's visible. Must contain:
+ *
x {Integer}: (required) the x coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
+ *
y {Integer}: (required) the y coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
+ *
w {Integer}: (required) the width of the object's collision box
+ *
h {Integer}: (required) the height the object's box
+ * @returns True if the object's collision box is within the camera's viewport.
+ */
+ objectIsVisible:function(obj) { return this.collides(obj,this._camera,0); },
+
+ // ---
+ // ---
+ // --- AUDIO ENGINE
+ // ---
+ // ---
+
+ _audiochannels:{},
+ _audiomastervolume:1.0,
+ _canaudio:false,
+ _audiodequeuetime:0,
+ _audioprefetch:0.5,
+ _audiocompatmode:0, // 0: pause/play, 1: google chrome compatibility, 2: ipad compatibility (single channel)
+ _createmode:0, // 0: clone, 1: rehinstance
+ _fakecheckprogressspeed:100, // Frequency of fake audio monitoring
+ _fakestoptime:1, // Fake audio stop for compatibility mode
+ _audioteam:2,
+ _loweraudioteam:1,
+ _audio:{lding:null,qtimer:false,aud:{},ast:{}},
+ _audioactions:[],
+ _showplayers:false,
+ _singlechannelname:"bgmusic",
+ _positiondelay:0,
+ _playerforcer:0,
+ _forcedmimeaudio:null,
+ _singlechannelaudio:false,
+ _audiomutevolume:0.0001, // Zero is still not accepted by everyone :(
+ _rawstopaudio:function(su) {
+ if (gbox._audiocompatmode==1) {
+ if (su.duration-su.currentTime>gbox._fakestoptime)
+ su.currentTime=su.duration-gbox._fakestoptime;
+ su.muted=true;
+ } else
+ su.pause();
+
+ },
+ _rawplayaudio:function(su) {
+ if (gbox._audiocompatmode==1) {
+ try { su.currentTime=0; } catch (e) {}
+ su.muted=false;
+ su.play();
+ } else if (gbox._audiocompatmode==2) {
+ su.load();
+ gbox._playerforcer=setInterval(function(e){try{su.play();clearInterval(gbox._playerforcer)}catch(e){}},1000);
+ } else {
+ try { su.currentTime=0; } catch (e) {}
+ su.play();
+ }
+ },
+ _finalizeaudio:function(ob,who,donext){
+
+ var cur=(who?who:this);
+ gbox.removeEventListener(cur,'ended', gbox._finalizeaudio);
+ gbox.removeEventListener(cur,'timeupdate', gbox._checkprogress);
+
+ gbox.addEventListener(cur,'ended', gbox._playbackended);
+ if (donext) gbox._loaderloaded();
+ },
+ _audiodoload:function() {
+ if (gbox._audiocompatmode==1) gbox._audio.lding.muted=true;
+ else if (gbox._audiocompatmode==2)
+ gbox._finalizeaudio(null,gbox._audio.lding,true);
+ else {
+ gbox._audio.lding.load();
+ gbox._audio.lding.play();
+ }
+ },
+ _timedfinalize:function() {
+ gbox._rawstopaudio(gbox._audio.lding);
+ gbox._finalizeaudio(null,gbox._audio.lding,true);
+ },
+ _checkprogress:function() {
+ if (gbox._audio.lding.currentTime>gbox._audioprefetch) gbox._timedfinalize();
+ },
+ _fakecheckprogress:function() {
+ if (gbox._audio.lding.currentTime>gbox._audioprefetch) gbox._timedfinalize(); else setTimeout(gbox._fakecheckprogress,gbox._fakecheckprogressspeed);
+ },
+ _audiofiletomime:function(f) {
+ var fsp=f.split(".");
+ switch (fsp.pop().toLowerCase()) {
+ case "ogg": { return "audio/ogg"; break }
+ case "mp3": { return "audio/mpeg"; break }
+ default: {
+ return "audio/mpeg";
+ }
+ }
+ },
+ _pushaudio:function(){try {this.currentTime=1.0} catch(e){} },
+ _createnextaudio:function(cau) {
+ if (cau.def) {
+ gbox.deleteAudio(cau.id);
+ this._audio.aud[cau.id]=[];
+ this._audio.ast[cau.id]={cy:-1,volume:1,channel:null,play:false,mute:false,filename:cau.filename[0]};
+ if (cau.def) for (var a in cau.def) this._audio.ast[cau.id][a]=cau.def[a];
+ }
+ if ((gbox._createmode==0)&&(cau.team>0)) {
+ var ael =this._audio.aud[cau.id][0].cloneNode(true);
+ gbox._finalizeaudio(null,ael,false);
+ } else {
+ var ael=document.createElement('audio');
+ ael.volume=gbox._audiomutevolume;
+ }
+ if (!gbox._showplayers) {
+ ael.style.display="none";
+ ael.style.visibility="hidden";
+ ael.style.width="1px";
+ ael.style.height="1px";
+ ael.style.position="absolute";
+ ael.style.left="0px";
+ ael.style.top="-1000px";
+ }
+ ael.setAttribute('controls',gbox._showplayers);
+ ael.setAttribute('aki_id',cau.id);
+ ael.setAttribute('aki_cnt',cau.team);
+ gbox.addEventListener(ael,'loadedmetadata', gbox._pushaudio); // Push locked audio in safari
+ if (((gbox._createmode==0)&&(cau.team==0))||(gbox._createmode==1)) {
+ if (gbox._forcedmimeaudio) {
+ for (var i=0;i0)
+ this._addqueue({t:1,a:ael});
+ }
+ }
+ },
+ setSplashSettings:function(a) { for (var n in a) this._splash[n]=a[n]; },
+ resetChannel:function(ch) {
+ if (this._canaudio&&this._audiochannels[ch])
+ if (ch=="master")
+ for (var ch in this._audiochannels)
+ this.setChannelVolume(ch,this._audiochannels[ch]._def.volume);
+ else if (this._audiochannels[ch])
+ this.setChannelVolume(ch,this._audiochannels[ch]._def.volume);
+ },
+ getChannelDefaultVolume:function(ch) {
+ if (this._canaudio&&this._audiochannels[ch]) return this._audiochannels[ch]._def.volume; else return null;
+ },
+ setChannelVolume:function(ch,a) {
+ if (this._canaudio&&this._audiochannels[ch]) {
+ if (ch=="master") this._audiomastervolume=a; else this._audiochannels[ch].volume=a
+ for (var j in gbox._audio.aud)
+ if (this._audio.ast[j].cy>-1) this._updateaudio(j);
+ }
+ },
+ getChannelVolume:function(ch) { if (ch=="master") return this._audiomastervolume; else if (this._audiochannels[ch]) return this._audiochannels[ch].volume; else return 0 },
+ changeChannelVolume:function(ch,a) {
+ if (this._canaudio&&this._audiochannels[ch]) {
+ var vol=this.getChannelVolume(ch)+a;
+ if (vol>1) vol=1; else if (vol<0) vol=0;
+ this.setChannelVolume(ch,vol);
+ }
+ },
+ stopChannel:function(ch) {
+ if (this._canaudio)
+ for (var j in gbox._audio.aud)
+ if (this._audio.ast[j].cy>-1&&gbox._audio.ast[j].play&&((ch=="master")||(this._audio.ast[j].channel==ch)))
+ this.stopAudio(j);
+ },
+
+ setAudioUnmute:function(a) { if (this._canaudio&&this._audio.ast[a]) { this._audio.ast[a].mute=false; this._updateaudio(a); } },
+ setAudioMute:function(a) { if (this._canaudio&&this._audio.ast[a]) { this._audio.ast[a].mute=true; this._updateaudio(a); } },
+ getAudioMute:function(a) { if (this._canaudio&&this._audio.ast[a]) return this._audio.ast[a].mute; else return null},
+
+ setAudioVolume:function(a,vol) { if (this._canaudio&&this._audio.ast[a]) { this._audio.ast[a].volume=vol; this._updateaudio(a); } },
+ getAudioVolume:function(a,vol) { if (this._canaudio&&this._audio.ast[a]) return this._audio.ast[a].volume; else return null},
+
+ setAudioPosition:function(a,p) { if (this._canaudio&&this._audio.ast[a]&&this._audio.aud[a][this._audio.ast[a].cy]) this._audio.aud[a][this._audio.ast[a].cy].currentTime=p;},
+ getAudioPosition:function(a) {if (this._canaudio&&this._audio.ast[a]&&this._audio.aud[a][this._audio.ast[a].cy]) if (this._audio.aud[a][this._audio.ast[a].cy].currentTime>this._positiondelay) return this._audio.aud[a][this._audio.ast[a].cy].currentTime-this._positiondelay; else return 0; else return 0},
+
+ getAudioDuration:function(a) {if (this._canaudio&&this._audio.ast[a]&&this._audio.aud[a][this._audio.ast[a].cy]) return this._audio.aud[a][this._audio.ast[a].cy].duration; else return 0},
+
+ changeAudioVolume:function(a,vol) { if (this._canaudio&&this._audio.ast[a]) { if (this._audio.ast[a].volume+vol>1) this._audio.ast[a].volume=1; else if (this._audio.ast[a].volume+vol<0) this._audio.ast[a].volume=0; else this._audio.ast[a].volume+=vol; this._updateaudio(a); } },
+ setCanAudio:function(a) { this._canaudio=!this._flags.noaudio&&a;},
+ setForcedMimeAudio:function(a){ this._forcedmimeaudio=a;},
+ setAudioChannels:function(a){
+ this._audiochannels=a;
+ for (var ch in a) {
+ this._audiochannels[ch]._def={};
+ for (var attr in this._audiochannels[ch])
+ if (attr!="_def") this._audiochannels[ch]._def[attr]=this._audiochannels[ch][attr];
+ }
+ },
+ setAudioTeam:function(a){ this._audioteam=a; },
+ setLowerAudioTeam:function(a){ this._loweraudioteam=a; },
+
+ // ---
+ // ---
+ // --- DYNAMIC SCRIPT INCLUSION
+ // ---
+ // ---
+
+ addScript:function(call) {
+ gbox._addtoloader({type:"script",call:call});
+ },
+
+ // ---
+ // ---
+ // --- BUNDLES
+ // ---
+ // ---
+
+ addBundle:function(call){
+ gbox._addtoloader({type:"bundle",call:call});
+ },
+
+ readBundleData:function(pack,call) {
+ // Local resources first
+ if (pack.setObject) for (var i=0;igbox.getScreenH()) l=gbox.getScreenH()-p;
+ tox.fillStyle = gbox.PALETTES.c64.colors[gbox.PALETTES.c64.order[Math.floor(Math.random()*gbox.PALETTES.c64.order.length)]];
+ tox.fillRect(0,p,gbox.getScreenW(),l);
+ p+=l;
+ }
+ tox.fillStyle = gbox.PALETTES.c64.colors.lightblue;
+ tox.fillRect(Math.floor(gbox.getScreenW()/10),Math.floor(gbox.getScreenH()/10),gbox.getScreenW()-Math.floor(gbox.getScreenW()/5),gbox.getScreenH()-Math.floor(gbox.getScreenH()/5));
+ if (gbox._splash.minilogo&&gbox.imageIsLoaded("logo")) {
+ var dw=gbox.getScreenW()/4;
+ var dh=(gbox.getImage("logo").height*dw)/gbox.getImage("logo").width;
+ gbox.blit(tox,gbox.getImage(gbox._splash.minilogo),{w:gbox.getImage("logo").width,h:gbox.getImage("logo").height,dx:(gbox.getScreenW()-dw)/2,dy:(gbox.getScreenH()-dh)/2,dw:dw,dh:dh});
+ }
+ break;
+ }
+ default:{
+ if (gbox._splash.background&&gbox.imageIsLoaded("_splash"))
+ gbox.blit(tox,gbox.getImage("_splash"),{w:gbox.getImage("_splash").width,h:gbox.getImage("_splash").height,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH()});
+ if (gbox._splash.minilogo&&gbox.imageIsLoaded("logo")) {
+ var dw=gbox.getScreenW()/4;
+ var dh=(gbox.getImage("logo").height*dw)/gbox.getImage("logo").width;
+ gbox.blit(tox,gbox.getImage(gbox._splash.minilogo),{w:gbox.getImage("logo").width,h:gbox.getImage("logo").height,dx:gbox.getScreenW()-dw-5,dy:gbox.getScreenH()-dh-5,dw:dw,dh:dh});
+ }
+ if (gbox._splash.footnotes&&gbox.imageIsLoaded("_dbf")) {
+ if (!gbox.getCanvas("_footnotes")) {
+ var fd=gbox.getFont("_dbf");
+ gbox.createCanvas("_footnotes",{w:gbox.getScreenW()-5,h:(gbox._splash.footnotes.length)*(fd.tileh+gbox._splash.footnotesSpacing)});
+ for (var i=0;i0?bw:0),gbox._splash.gaugeHeight-2);
+ }
+ }
+ }
+ tox.restore();
+ gbox.setStatBar("Loading... ("+gbox._loaderqueue.getDone()+"/"+gbox._loaderqueue.getTotal()+")");
+ setTimeout(gbox._waitforloaded,50);
+ } else {
+ gbox.deleteImage("_splash");
+ gbox.setStatBar();
+ gbox._cb();
+ }
+ },
+ clearCache:function() { this._loadercache.clear(); },
+
+ // ---
+ // ---
+ // --- BROWSER QUIRKS
+ // ---
+ // ---
+
+ checkCanvasSupport:function() {
+ return !!document.createElement('canvas').getContext;
+ },
+ addEventListener:function(to,event,code) {
+ if (to.addEventListener) to.addEventListener(event,code,false);
+ else to.attachEvent('on'+event,code);
+ },
+ removeEventListener:function(to,event,code) {
+ if (to.removeEventListener) to.removeEventListener(event,code,false);
+ else to.detachEvent('on'+event,code);
+ },
+ XMLHttpFactories:[
+ function () {return new XMLHttpRequest()},
+ function () {return new ActiveXObject("Msxml2.XMLHTTP")},
+ function () {return new ActiveXObject("Msxml3.XMLHTTP")},
+ function () {return new ActiveXObject("Microsoft.XMLHTTP")}
+ ],
+ createXmlHttpRequest:function() {
+ var xmlhttp=false;
+ /* running locally on IE5.5, IE6, IE7 */ ; /*@cc_on
+ if(location.protocol=="file:"){
+ if(!xmlhttp)try{ xmlhttp=new ActiveXObject("MSXML2.XMLHTTP"); }catch(e){xmlhttp=false;}
+ if(!xmlhttp)try{ xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); }catch(e){xmlhttp=false;}
+ } ; @cc_off @*/
+ /* IE7, Firefox, Safari, Opera... */
+ if(!xmlhttp)try{ xmlhttp=new XMLHttpRequest(); }catch(e){xmlhttp=false;}
+ /* IE6 */
+ if(typeof ActiveXObject != "undefined"){
+ if(!xmlhttp)try{ xmlhttp=new ActiveXObject("MSXML2.XMLHTTP"); }catch(e){xmlhttp=false;}
+ if(!xmlhttp)try{ xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); }catch(e){xmlhttp=false;}
+ }
+ /* IceBrowser */
+ if(!xmlhttp)try{ xmlhttp=createRequest(); }catch(e){xmlhttp=false;}
+ return xmlhttp;
+ }
+
+};
+
diff --git a/deps/akihabara-core-1.3.1/akihabara/help.js b/deps/akihabara-core-1.3.1/akihabara/help.js
new file mode 100644
index 0000000..2c4c5fa
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/akihabara/help.js
@@ -0,0 +1,608 @@
+// ---
+// Copyright (c) 2010 Francesco Cottone, http://www.kesiev.com/
+// ---
+
+/**
+ * @namespace Help module provides some Javascript-specific functions, such object copying, randomizing functions,
+ * string/array handlers and the akihabaraInit function.
+ */
+var help={
+
+ /**
+ * Searches an object in an array filtering for one of their properties.
+ * @param {Array} a The array.
+ * @param {String} field The searched field.
+ * @param {String} value The searched value.
+ * @returns The found object, otherwise null.
+ */
+ searchObject:function(a,field,value) {
+ if (!a) return null; else
+ for (var i=0;i0 means you're falling onto something else.
+ * @param {Object} th The object that is (possibly) being jumped on.
+ * @param {Object} by The object doing the jumping-on.
+ * @returns True if the two objects are overlapping enough and by.accy>0.
+ */
+ isSquished:function(th,by) {
+ return ((by.accy>0)&&gbox.collides(th,by)&&(Math.abs(th.y-(by.y+by.h))<(th.h/2)))
+ },
+
+ /**
+ * Generates uniformly distributed random integers between min and min+range, non-inclusive. So help.random(0,2) will only return 0 and 1, etc.
+ * @param {Integer} min The minimum random value to be returned by the function.
+ * @param {Integer} range The number of different values returned by the function.
+ * @returns An integer between min (includive) and min+range (noninclusive).
+ */
+ random:function(min,range) {
+ return min+Math.floor(Math.random()*range);
+ },
+
+
+ /**
+ * Determines which frame of a given animation to display. Will loop an animation.
+ * @param {Integer} cnt A global frame counter.
+ * @param {Object} anim An object with parameters speed (the animation speed) and frames (the array representing the animation sequence).
+ * @returns The particular animation frame to display during this step.
+ */
+ decideFrame:function(cnt,anim) {
+ return anim.frames[Math.floor(cnt/anim.speed)%anim.frames.length];
+ },
+
+ /**
+ * Determines which frame of a given animation to display. Will remain on the last frame when the animation has played once.
+ * @param {Integer} cnt A global frame counter.
+ * @param {Object} anim An object with parameters speed (the animation speed) and frames (the array representing the animation sequence).
+ * @returns The particular animation frame to display during this step.
+ */
+ decideFrameOnce:function(cnt,anim) {
+ return anim.frames[(cnt>=anim.frames.length*anim.speed?anim.frames.length-1:Math.floor(cnt/anim.speed))];
+ },
+
+ /**
+ * Returns whether the animation was fully played at least once with decideFrame or fully with decideFrameOnce.
+ * @param {Integer} cnt A global frame counter.
+ * @param {Object} anim An object with parameters speed (the animation speed) and frames (the array representing the animation sequence).
+ * @returns A boolean, true if the animation has been played at least once.
+ */
+ isLastFrameOnce:function(cnt,anim) {
+ return (cnt>=anim.frames.length*anim.speed);
+ },
+
+ /**
+ * Given an incrementing value each step, this will return a value increasing from 0 until max/2, at which point it will decrement to 0, then go back up to max/2, in an endless cycle.
+ * @param {Integer} counter A counter.
+ * @param {Integer} max This determines the period of the function -- assuming counter is incrementing by one, a complete back-and-forth will take 'max' steps.
+ * @returns An integer.
+ */
+ upAndDown:function(counter,max) {
+ if ((counter%max)>(max/2)) return max-(counter%max); else return (counter%max);
+ },
+
+ /**
+ * Given x,y coordinates and map information, this returns the tile at a given point.
+ * @param {Integer} x An x-coordinate.
+ * @param {Integer} y A y-coordinate.
+ * @param {Object} map The map object.
+ * @param {Object} ifout An object or value to be returned if the x,y coordinate pair is outside the map.
+ * @param {String} mapid The id for the map array within the map object. Default is 'map'.
+ * @returns An integer representing the value of the tile in the map array at that x,y coordinate. If there is no tile, null is returned.
+ */
+ getTileInMap:function(x,y,map,ifout,mapid) {
+ if (!mapid) mapid="map";
+ var ts=gbox._tiles[map.tileset];
+ var tx=Math.floor(x/ts.tilew);
+ var ty=Math.floor(y/ts.tileh);
+ if ((ty<0)||(ty>=map[mapid].length)) return ifout; else
+ if ((tx<0)||(tx>=map[mapid][ty].length)) return ifout; else
+ return map[mapid][ty][tx];
+ },
+
+ /**
+ * Takes an ascii-art-style array of characters and converts it to an Akihabara-compatible map format.
+ * @param {Array} map An array of characters representing a map.
+ * @param {Array} tra A translation array. This is an array of arrays, formatted like [ [null, char1], [0, char2], [1, char3] ]. There must at least be a null entry, followed by one numerical entry for each tile type you want to render, corresponding to the unique characters in the map array. The null entry maps a character to empty space.
+ * @returns A map array formatted such that it can be attached to a map object.
+ */
+ asciiArtToMap:function(map,tra) {
+ var sz=tra[0][1].length;
+ var ret=[];
+ var xpos;
+ var pie;
+ for (var y=0;ymax.
+ */
+ limit:function(v,min,max) {
+ if (vmax) return max; else return v;
+ },
+
+ /**
+ * Subtracts or adds 1 to a value, always converging to zero. For example, passing -3 yields -2, 5 yields 4, etc. Works best with integers.
+ * @param {Integer} v A value.
+ * @returns A value that is one closer to 0 on the number line than v.
+ */
+ goToZero:function(v) { return (v?v-(v/Math.abs(v)):0); },
+
+ /**
+ * Merges two sets of parameters together without overwriting existing parameters. This merges from model to data, and if data and model share parameters, data's values remain intact.
+ * @param {Object} data An object containing a set of parameters, the destination of the merge.
+ * @param {Object} model An object containing a set of parameters, the source of the merge.
+ * @returns A merged model where the values of 'data' remain untouched: only new parameters and values from 'model' make it in.
+ * @example
+ * dst = {a: 1, b: 2, c: "three"};
+ * src = {c: "three", d: "four"};
+ * merged = help.mergeWithModel(dst,src);
+ * merged; // => {a: 1, b: 2, c: 3, d: "four"};
+ */
+ mergeWithModel:function(data,model) {
+ if (data==null) data={};
+ if (model!=null)
+ for (var i in model)
+ if (data[i]==null) data[i]=model[i];
+ return data;
+ },
+
+ /**
+ * Merges two sets of parameters together overwriting any existing parameters. This merges model->data, and if data and model share parameters, data's are overwritten by model's.
+ * @param {Object} data An object containing a set of parameters, the destination of the merge.
+ * @param {Object} model An object containing a set of parameters, the source of the merge.
+ * @returns A merged model where the values of 'model' take precedence over those of 'data'. The 'data' object is returned and will be an exact copy of 'model', plus any parameters that 'data' had before the merge that 'model' did not.
+ * @example
+ * dst = {a: 1, b: 2, c: "three"};
+ * src = {c: "three", d: "four"};
+ * merged = help.mergeWithModel(dst,src);
+ * merged; // => {a: 1, b: 2, c: "three", d: "four"}
+ */
+ copyModel:function(data,model) {
+ if (data==null) data={};
+ if (model!=null)
+ for (var i in model) data[i]=model[i];
+ return data;
+ },
+
+ /**
+ * Creates a subset of an existing set of parameters.
+ * @param {Object} obj An object containing a set of parameters, the source of the data.
+ * @param {Array} attrs An array of strings, containing the names of parameters you wish to copy.
+ * @returns A new set of parameters based on the subset specified.
+ * @example
+ * data = {a: 1, b: 2, c: "three"};
+ * newdata = help.createModel(data, ["a", "c"]);
+ * newdata; // => {a: 1, c: "three"}
+ */
+ createModel:function(obj,attrs) {
+ var ret={};
+ for (var i=0;i {a: 1, b: 2, c: "three"}
+ */
+ cloneObject:function(model) {
+ if (!model) return model;
+ var data={};
+ for (var i in model) data[i]=model[i];
+ return data;
+ },
+
+ /**
+ * Sets a tile in the map and draws it. Does not return anything.
+ * @param {Object} ctx The canvas context for the map. Accessed via gbox.getCanvasContext("canvasname")
+ * @param {Object} map The game map object.
+ * @param {Integer} x The index of the tile column within the map array -- so a 1 would mean the second column of tiles.
+ * @param {Integer} y The index of the tile row within the map array -- so a 1 would mean the second row of tiles.
+ * @param {Integer} tile The integer representing the new tile you wish to draw. This is its index within the tileset; a null value will erase whatever tile is present.
+ * @param {String} The ID of the map. Defaults to 'map'.
+ * @example
+ * // Remove the second tile to the right and down from the upper left corner of the tile map. Assumes our map canvas is called 'map_canvas'.
+ * help.setTileInMap(gbox.getCanvasContext("map_canvas"),map,1,1,null,"map");
+ */
+ setTileInMap:function(ctx,tilemap,x,y,tile,map) {
+ var ts=gbox.getTiles(tilemap.tileset);
+ tilemap[(map==null?"map":map)][y][x]=tile;
+ if (tile==null)
+ gbox.blitClear(ctx,{x:x*ts.tilew,y:y*ts.tilew,h:ts.tileh,w:ts.tilew});
+ else
+ gbox.blitTile(ctx,{tileset:tilemap.tileset,tile:tile,dx:x*ts.tilew,dy:y*ts.tilew});
+ },
+
+ /**
+ * Sets a tile in the map and draws it using pixels as coords. Does not return anything.
+ * @param {Object} ctx The canvas context for the map. Accessed via gbox.getCanvasContext("canvasname")
+ * @param {Object} map The game map object.
+ * @param {Integer} x The index of the pixel column within the map array -- so a 1 would mean the second column of tiles.
+ * @param {Integer} y The index of the pixel row within the map array -- so a 1 would mean the second row of tiles.
+ * @param {Integer} tile The integer representing the new tile you wish to draw. This is its index within the tileset; a null value will erase whatever tile is present.
+ * @param {String} The ID of the map. Defaults to 'map'.
+ */
+ setTileInMapAtPixel:function(ctx,tilemap,x,y,tile,map) {
+ var ts=gbox.getTiles(tilemap.tileset);
+ x=Math.floor(x/ts.tilew);
+ y=Math.floor(y/ts.tileh);
+ help.setTileInMap(ctx,tilemap,x,y,tile,map);
+ },
+
+
+ /**
+ * Returns the Nth element in an array. If the array is shorter than N, it returns the last element of the array.
+ * @param {Array} a An array.
+ * @param {Integer} id An index to the array.
+ * @returns If id > a.length, it returns a[a.length-1]. Otherwise returns a[id].
+ */
+ getArrayCapped:function(a,id) {
+ if (id>=a.length) return a[a.length-1]; else return a[id];
+ },
+
+ /**
+ * Returns the element of a sorted array that have the highest value of one of the properties.
+ * @param {Array} a An array.
+ * @param {Integer} value The target value.
+ * @param {String} field The property used to filter the array.
+ * @returns The object with the highest target value, otherwise the first element of the array.
+ */
+ getArrayIndexed:function(a,value,field) {
+ if (a[0][field]==null) return a[0];
+ var i=0;
+ while ((value>a[i][field])&&(i!=a.length-1)) i++;
+ return a[i];
+ },
+
+
+ /**
+ * Converts a quantity of frames into a timestamp formatted "mm:ss:cs" (minutes, seconds, centiseconds). Calculated using the current frames per second.
+ * @param {Integer} frames A quantity of frames.
+ * @returns A string containing a timestamp formatted "mm:ss:cs", representing the length of time it would take to render that many frames.
+ * @example
+ * // Assuming 25 frames per second, Akihabara's default.
+ * timestamp = help.framestotime(25);
+ * timestamp; // => '00:01:00';
+ * timestamp = help.framestotime(25 * 60);
+ * timestamp; // => '01:00:00';
+ */
+ framestotime:function(frames) {
+ var csec=Math.ceil(frames/gbox.getFps()*100);
+ return this.prepad((Math.floor(csec/6000)%60),2,"0")+":"+this.prepad((Math.floor(csec/100)%60),2,"0")+":"+this.prepad(csec%100,2,"0");
+
+ },
+
+ /**
+ * Reads the value of a query parameter from the URL of the web page.
+ * @param {String} name The name of the URL parameter.
+ * @returns The value of the URL parameter, as a string.
+ * @example
+ * // If the URL is http://example.com/game.html?lives=3
+ * player.lives = help.geturlparameter("lives");
+ * player.lives; // => 3
+ */
+ geturlparameter:function( name ) {
+ name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
+ var regexS = "[\\?&]"+name+"=([^]*)";
+ var regex = new RegExp( regexS );
+ var results = regex.exec( window.location.href );
+ if( results == null )
+ return "";
+ else
+ return results[1];
+ },
+
+ /**
+ * Writes the contents of an object to a string. Useful for debugging.
+ * @param {Object} Any object.
+ * @returns A string containing all the contents of an object. If the object contains functions, the string will contain the code for those functions.
+ */
+ objToStr:function(o) {
+ var ret="";
+ for (var n in o) ret+=n+":["+o[n]+"] ";
+ return ret;
+ },
+
+ /**
+ * Tests whether an object contains a given parameter.
+ * @param {Object} A reference to a parameter of an object.
+ * @returns True if the object contains that parameter, false if it does not.
+ * @example
+ * foo = {a: 1, b: 2};
+ * help.isDefined(foo.a); // => true
+ * help.isDefined(foo.c); // => false
+ */
+ isDefined:function(v) {
+ return ((typeof(v) !== 'undefined') || (v===null));
+ },
+
+ /**
+ * Automatically configures a bunch of settings depending on the web browser and device that is viewing the game. Mostly sets the maximum number of audio channels and touch settings.
+ */
+ getDeviceConfig:function() {
+ var cap;
+ if (navigator.userAgent.match(/nintendo wii/i))
+ cap={iswii:true,height:window.innerHeight,doublebuffering:true} // Simulated double buffering has been resumed. Canvas on Opera for Wii has a strange sprite blinking effect - usually browsers render frames once ended and this is an exception.
+ else if (navigator.userAgent.match(/iPhone/i)||navigator.userAgent.match(/iPod/i) || navigator.userAgent.match(/Android/i))
+ cap={touch:true,width:320};
+ else if (navigator.userAgent.match(/iPad/i))
+ cap={touch:true,width:768,forcedidle:10}; // Forced idle time is needed for correct framerate calculation.
+ else
+ cap={zoom:2};
+
+ cap.canaudio=!!(document.createElement('audio').canPlayType);
+ if (cap.canaudio) {
+ if (navigator.userAgent.match(/iPad/i)||navigator.userAgent.match(/iPhone/i)||navigator.userAgent.match(/iPod/i)) {
+ cap.audiocompatmode=2; // Audio loading mode.
+ cap.audioteam=1; // Only a member is required in the audioteam.
+ cap.audioisexperimental=true; // Audio is experimental, since limited.
+ cap.audioissinglechannel=true; // Single channeled. Plays only the "bgmusic" channel.
+ } else if (navigator.userAgent.match(/Chrome/i)) {
+ cap.audioteam=3; // Quite low performance on playback responsiveness.
+ } else if (navigator.userAgent.match(/Firefox/i)) {
+ cap.audioteam=1; // Testing smaller audioteam
+ cap.audiopositiondelay=0.3; // Ogg playback is slower 0.3 between MP3 playback. Don't know why :)
+ cap.audiocreatemode=1; // Firefox is stalling while downloading lot of things
+ } else if (navigator.userAgent.match(/Minefield/i)) {
+ cap.audioteam=1; // Testing smaller audioteam
+ cap.audiocreatemode=1; // Firefox is stalling while downloading lot of things
+ // Minefield has fixed the 0.3 delay!
+ } else if (navigator.userAgent.match(/khtml/i)&&navigator.userAgent.match(/konqueror/i)) {
+ // Note that audio is not supported in applewebkit mode :(
+ cap.audioteam=1;
+ cap.audioissinglechannel=true; // Single channeled. Plays only the "bgmusic" channel.
+ cap.audiocompatmode=2; // Sorry. iPad single channel mode. Audio events are not triggered properly and audio properties are missing so many audio feautres are not available. :(
+ cap.forcedmimeaudio="audio/ogg"; // Usually OGG audio playback is supported by default in KDE env.
+ cap.audioisexperimental=true; // Audio is experimental, since limited.
+ } else if (navigator.userAgent.match(/Safari/i)) {
+ cap.audioteam=1; // Testing smaller audioteam
+ } else if (navigator.userAgent.match(/Opera/i)) {
+ cap.audioteam=1; // Testing smaller audioteam
+ cap.audiocreatemode=1; // Do not like audio object cloning very much
+ } else if (navigator.userAgent.match(/MSIE 9.0/i)||navigator.userAgent.match(/MSIE 7.0/i)) {
+ cap.audioteam=2;
+ cap.audiocompatmode=1; // Audio loading mode.
+ cap.audioisexperimental=navigator.userAgent.match(/MSIE 7.0/i); // Audio is experimental, on IE7 (for compat mode)
+ } else
+ cap.audioisexperimental=true; // Audio is just experimental on all other devices.
+ }
+ return cap;
+ },
+
+ /**
+ * This provides a number of configurations: fps, display zoom, dynamic frameskip, force touch parameters, etc. Many of these settings can
+ * be set manually by passing an object with the parameters defined, or via URL parameters.
+ * @param {Object} data An optional object containing parameters you wish to set. Works for data.zoom, data.splash, data.width, data.height, data.title, data.fps, and data.padmode.
+ */
+ akihabaraInit:function(data) {
+ if ((typeof data).toLowerCase() == "string") data={title:data};
+ var device=this.getDeviceConfig();
+ var footnotes=["MADE WITH AKIHABARA (C)2010 - GPL2/MIT","Project: www.kesiev.com/akihabara","Sources: github.com/kesiev/akihabara"];
+ document.title=(data.title?data.title:"Akihabara");
+ if (data.splash) {
+ if (data.splash.footnotes)
+ for (var i=0;iiphopad._width-iphopad._buttonsize)
+ nb.a=true;
+ else if (rp.x>iphopad._width-iphopad._buttonsize2)
+ nb.b=true;
+ else if (rp.x>iphopad._width-iphopad._buttonsize3)
+ nb.c=true;
+
+ }
+ this._swap=!this._swap;
+ for (var i in this._cross) {
+ if (nc[i]!=iphopad._cross[i])
+ if (nc[i]) gbox._keydown({fake:true,keyCode:gbox._keymap[i]});
+ else gbox._keyup({fake:true,keyCode:gbox._keymap[i]});
+ }
+ for (var i in this._buttons) {
+ if (nb[i]!=iphopad._buttons[i])
+ if (nb[i]) gbox._keydown({fake:true,keyCode:gbox._keymap[i]});
+ else gbox._keyup({fake:true,keyCode:gbox._keymap[i]});
+ }
+
+ iphopad._cross=nc;
+ iphopad._buttons=nb;
+ },
+ _fakelisten:function(e) {
+ iphopad._listen({
+ touches:[
+ {
+ pageX:e.clientX,
+ pageY:e.clientY
+ }
+ ]
+ });
+ },
+
+ /**
+ * Initializes the game controls for use with an I-product or Android device.
+ * @param {Object} data passes in information about the screen and its traits such as size.
+ */
+ initialize:function(data) {
+ var oElement=document.createElement("div");
+ oElement.style.margin="auto";
+ oElement.style.padding="0px";
+ oElement.style.height=data.h+"px";
+ oElement.style.width="100%";
+ oElement.style.backgroundImage="url("+data.bg+")";
+ oElement.style.backgroundRepeat="repeat-x";
+
+ var tpad=document.createElement("div");
+ tpad.style.cssFloat="left";
+ tpad.style.padding="0px";
+ tpad.style.margin="0px";
+ tpad.style.height=data.h+"px";
+ tpad.style.width=data.h+"px";
+ tpad.style.backgroundImage="url("+data.dpad+")";
+ tpad.style.backgroundRepeat="no-repeat";
+
+ var bpad=document.createElement("div");
+ bpad.style.cssFloat="right";
+ bpad.style.padding="0px";
+ bpad.style.margin="0px";
+ bpad.style.height=data.h+"px";
+ bpad.style.width=iphopad._buttonsize3+"px";
+ bpad.style.backgroundImage="url("+data.buttons+")";
+ bpad.style.backgroundRepeat="no-repeat";
+
+ oElement.appendChild(tpad);
+ oElement.appendChild(bpad);
+ gbox._box.appendChild(oElement);
+
+ oElement.ontouchstart=function(evt) { evt.preventDefault();evt.stopPropagation(); iphopad._listen(evt) };
+ oElement.ontouchend=function(evt) { evt.preventDefault();evt.stopPropagation();iphopad._listen(evt) };
+ oElement.ontouchmove=function(evt) { evt.preventDefault();evt.stopPropagation();iphopad._listen(evt) };
+ //oElement.onmousemove=function(evt) { iphopad._fakelisten(evt) };
+ var sizes=gbox._domgetabsposition(oElement);
+ this._gapx=sizes.x;
+ this._gapy=sizes.y;
+ this._width=sizes.w;
+ this._height=sizes.h;
+ this._center={x:Math.floor(this._height/2),y:Math.floor(this._height/2)};
+ }
+
+}
diff --git a/deps/akihabara-core-1.3.1/akihabara/padbg.png b/deps/akihabara-core-1.3.1/akihabara/padbg.png
new file mode 100644
index 0000000..09b9db0
Binary files /dev/null and b/deps/akihabara-core-1.3.1/akihabara/padbg.png differ
diff --git a/deps/akihabara-core-1.3.1/akihabara/splash.png b/deps/akihabara-core-1.3.1/akihabara/splash.png
new file mode 100644
index 0000000..5d031ce
Binary files /dev/null and b/deps/akihabara-core-1.3.1/akihabara/splash.png differ
diff --git a/deps/akihabara-core-1.3.1/akihabara/tool.js b/deps/akihabara-core-1.3.1/akihabara/tool.js
new file mode 100644
index 0000000..9f41b2e
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/akihabara/tool.js
@@ -0,0 +1,131 @@
+// ---
+// Copyright (c) 2010 Francesco Cottone, http://www.kesiev.com/
+// ---
+
+/**
+ * @namespace Tool module provides simple developing tools. Currently, this file only has a cel-composer:
+ * it can compose an image stacking a set of frames for animating objects, applying a
+ * number of filters to each frame.
+ */
+var tool={
+ _images:[],
+ _loadedflag:[],
+ _data:{},
+ _count:0,
+ _countloaded:0,
+
+ /**
+ * This function documents that an image in an animation sequence is loaded and checks if the other images are loaded or not
+ * @param {Object} id This is the object which is used as an id for keeping track of things related to this object in this function
+ */
+ _loaded:function(id) {
+ this._loadedflag[id]=true;
+ tool._countloaded++;
+ document.title=tool._countloaded+"/"+tool._count;
+ for (var i=0;i
+ *
x{Integer}: (required)Objects x position
+ *
y{Integer}: (required)Objects y position
+ *
z{Integer}: (required)Objects z position
+ *
colx{Integer}: (required)The dimension of the collision box along the x axis
+ *
coly{Integer}: (required)The dimension of the collision box along the y axis
+ *
colh{Integer}: (required)Collision box height
+ *
colw{Integer}: (required)Collision box width
+ *
+ * @param {Object} to The object that collision is being checked against.
+ *
+ *
x{Integer}: (required)Objects x position
+ *
y{Integer}: (required)Objects y position
+ *
z{Integer}: (required)Objects z position
+ *
colx{Integer}: (required)Collision x
+ *
coly{Integer}: (required)Collision y
+ *
colh{Integer}: (required)Collision box height
+ *
colw{Integer}: (required)Collision box width
+ *
+ * @param {int} t This is the tollerance (or margin for error) on the collide function.
+ */
+ collides:function(fr,to,t) { // Special collision. Counts also the Z
+ if (Math.abs(fr.z,to.z)<5) return gbox.collides({x:fr.x+fr.colx,y:fr.y+fr.coly,h:fr.colh,w:fr.colw},{x:to.x+to.colx,y:to.y+to.coly,h:to.colh,w:to.colw},t); else return false;
+ },
+
+ /**
+ * Checks for pixel collisions with an offset to the X and Y of the colidable using colx and coly.
+ * @param {Object} fr The object which collision is being tested for.
+ * @param {Object} to The object (or point) which collision is being tested against.
+ * @param {int} t The tollerance of the collision algorithm.
+ */
+ pixelcollides:function(fr,to,t) { // Special collision. Counts also the Z
+ return gbox.pixelcollides(fr,{x:to.x+to.colx,y:to.y+to.coly,h:to.colh,w:to.colw},t);
+ },
+
+ /**
+ * Initializes the game with the variables needed for topview and whatever else you feed in through data.
+ * @param {Object} th Passes in the object being initialized.
+ * @param {Object} data This is used to pass in everything that's being initiliized. If a value is not in Data then a default value is used instead. This can pass in values which do not have a default.
+ *
+ *
x{Integer}: x position of the object. (defaults to 0)
+ *
y{Integer}: y position of the object. (defaults to 0)
+ *
z{Integer}: z index of the object. (defaults to 0)
+ *
accx{Integer}: The starting x velociyt of the object. (defaults to 0)
+ *
accy{Integer}: The starting y velocity of the object. (defaults to 0)
+ *
accz{Integer}: The starting z velocity of the object. (defaults to 0)
+ *
frames{Object}: This is stores the animation frames for the objects in a map style structure. An empty map means the default image will display with no animation frames. (defaults to an empty map)
+ *
shadow: (defaults to null)
//incomplete
+ *
maxacc{Integer}: (defaults to )4
+ *
controlmaxacc{Integer}: (defaults to 4)
+ *
responsive: (defaults to 0)
+ *
weapon: (defaults to 0)
+ *
camera{Boolean}: (defaults to true)
+ *
flipv{Boolean}: Notes if the object is flipped vertically(defaults to false)
+ *
fliph{Boolean}: Notes if the object is flipped horrizontally(defaults to false)
+ *
facing{Integer}: Stores the facing of the object. This is set with pre-defined Integer values from within Toys.(defaults to toys.FACE_DOWN)
+ *
+ *
FACE_UP:0,
+ *
FACE_RIGHT:1,
+ *
FACE_DOWN:2,
+ *
FACE_LEFT:3,
+ *
+ *
flipside{Boolean}: (defaults to true)
+ *
haspushing{Boolean}: (defaults to false)
+ *
frame: (default to 0)
+ *
colh{Integer}: (defaults to gbox.getTiles(th.tileset).tilehh)
+ *
colw{Integer}: (defaults to gbox.getTiles(th.tileset).tilew)
+ *
colx{Integer}: (defaults to 0)
+ *
staticspeed{Integer}: (defaults to 0)
+ *
nodiagonals{Boolean}: (defaults to false)
+ *
noreset: (defaults to false)
+ *
+ */
+ initialize:function(th,data) {
+ help.mergeWithModel(
+ th,
+ help.mergeWithModel(
+ data,
+ {
+ x:0, y:0,
+ z:0,
+ accx:0, accy:0, accz:0,
+ frames:{},
+ shadow:null,
+ maxacc:4, controlmaxacc:4,
+ responsive:0, // Responsiveness
+ weapon:0, // Weapon
+ camera:true,
+ flipv:false, fliph:false,
+ facing:toys.FACE_DOWN,
+ flipside:true,
+ haspushing:false,
+ frame:0,
+ colh:gbox.getTiles(th.tileset).tilehh,
+ colw:gbox.getTiles(th.tileset).tilew,
+ colx:0,
+ staticspeed:0,
+ nodiagonals:false,
+ noreset:false
+ }
+ )
+ );
+ if (th.coly==null) th.coly=gbox.getTiles(th.tileset).tileh-th.colh;
+ th.colhh=Math.floor(th.colh/2);
+ th.colhw=Math.floor(th.colw/2);
+
+ toys.topview.spawn(th);
+ },
+
+ /**
+ * Spawns a new object in the topview namespace. This also merges parameters in data into paramaters in th using help.copyModel.
+ * This initializes some basic basic variables for the object and sets the Z index.
+ * @param {Object} th References 'this' which is the object that called the method (generally).
+ *
+ *
y {Integer}: (required) The object's y position.
+ *
h {Integer}: (required) The object's height.
+ *
+ * @param {Object} data This holds variables to be merged into th's stored info.
+ */
+ spawn:function(th,data) {
+ th.xpushing=toys.PUSH_NONE; // user is moving side
+ th.vpushing=toys.PUSH_NONE; // user is moving side
+ th.zpushing=toys.PUSH_NONE; // user is moving side
+ th.counter=0; // self counter
+ th.hittimer=0;
+ th.killed=false;
+ help.copyModel(th,data);
+ gbox.setZindex(th,th.y+th.h); // these object follows the z-index and uses ZINDEX_LAYER
+ },
+
+ /**
+ * This sets and runs the control keys for the game.
+ * @param {Object} th This is the object that is being controlled by the keys (assumed to be the player)
+ *
+ *
accx: the object's currect acceleration in the x direction
+ *
accy: the object's currect acceleration in the y direction
+ *
responsive: minimum movement speed
+ *
staticspeed: turns off acceleration
+ *
nodiagonals: boolean determining if the object can move along both axis at once.
+ *
xpushing: a boolean that notes whether the object is pushing against something in the x direction.
+ *
ypushing: a boolean that notes whether the object is pushing against something in the y direction.
+ *
controlmaxacc: max acceleration for the object along an axis
+ *
noreset: checks for the object being allowed to reset its pushing status (?)
+ *
+ * @param {Object} keys These are the control keys being passed in for left, right, up, and down.
+ * //incomplete
+ */
+ controlKeys:function(th,keys) {
+ var cancelx=false;
+ var cancely=false;
+ var idlex=false;
+ var idley=false;
+
+ if (gbox.keyIsPressed(keys.left)||keys.pressleft) {
+ th.xpushing=toys.PUSH_LEFT;
+ th.facing=toys.FACE_LEFT;
+ if (th.accx>th.responsive) th.accx=th.responsive;
+ if (th.staticspeed) th.accx=-th.staticspeed; else th.accx=help.limit(th.accx-1,-th.controlmaxacc,th.controlmaxacc);
+ if (th.nodiagonals) { cancely=true; idley=true }
+ } else if (gbox.keyIsPressed(keys.right)||keys.pressright) {
+ th.xpushing=toys.PUSH_RIGHT;
+ th.facing=toys.FACE_RIGHT;
+ if (th.accx<-th.responsive) th.accx=-th.responsive;
+ if (th.staticspeed) th.accx=th.staticspeed; else th.accx=help.limit(th.accx+1,-th.controlmaxacc,th.controlmaxacc);
+ if (th.nodiagonals) { cancely=true; idley=true }
+ } else idlex=true;
+
+ if (!cancely&&(gbox.keyIsPressed(keys.up)||keys.pressup)) {
+ th.ypushing=toys.PUSH_UP;
+ th.facing=toys.FACE_UP;
+ if (th.accy>th.responsive) th.accy=th.responsive;
+ if (th.staticspeed) th.accy=-th.staticspeed; else th.accy=help.limit(th.accy-1,-th.controlmaxacc,th.controlmaxacc);
+ if (th.nodiagonals) { cancelx=true; idlex=true; }
+ } else if (!cancely&&(gbox.keyIsPressed(keys.down)||keys.pressdown)) {
+ th.ypushing=toys.PUSH_DOWN;
+ th.facing=toys.FACE_DOWN;
+ if (th.accy<-th.responsive) th.accy=-th.responsive;
+ if (th.staticspeed) th.accy=th.staticspeed; else th.accy=help.limit(th.accy+1,-th.controlmaxacc,th.controlmaxacc);
+ if (th.nodiagonals) { cancelx=true; idlex=true; }
+ } else idley=true;
+
+
+
+ if (idlex) {
+ if (cancelx) th.accx=0;
+ if (cancelx||!th.noreset) th.xpushing=toys.PUSH_NONE;
+ }
+ if (idley) {
+ if (cancely) th.accy=0;
+ if (cancely||!th.noreset) th.ypushing=toys.PUSH_NONE;
+ }
+ },
+
+ /**
+ * Gets the next X position the object is going to move to.
+ * @param {Object} th The object being checked.
+ *
+ *
x: the current x position of the object
+ *
accx: the object's currect acceleration in the x direction
+ *
maxacc: the max accleration the object can have (if accx is greater than this then this value is used instead)
+ *
+ */
+ getNextX:function(th) { return th.x+help.limit(th.accx,-th.maxacc,th.maxacc); },
+
+ /**
+ * Gets the next Y position the object is going to move to.
+ * @param {Object} th The object being checked.
+ *
+ *
y: the current y position of the object
+ *
accy: the object's currect acceleration in the y direction
+ *
maxacc: the max accleration the object can have (if accy is greater than this then this value is used instead)
+ *
+ */
+ getNextY:function(th) { return th.y+help.limit(th.accy,-th.maxacc,th.maxacc); },
+
+ /**
+ * Gets the next Z position the object is going to move to.
+ * @param {Object} th The object being checked.
+ *
+ *
z: the current z position of the object
+ *
accz: the object's currect acceleration in the z direction
+ *
maxacc: the max accleration the object can have (if accz is greater than this then this value is used instead)
+ *
+ */
+ getNextZ:function(th) { return th.z+help.limit(th.accz,-th.maxacc,th.maxacc); },
+
+ /**
+ * Sets the objects current location to its next location using the getNextX and getNextY methods.
+ * @param {Object} th The object being modified.
+ *
+ *
x: the current x position of the object
+ *
y: the current y position of the object
+ *
accx: the object's currect acceleration in the x direction
+ *
accy: the object's currect acceleration in the y direction
+ *
maxacc: the max accleration the object can have (if either acceleration is greater than this then this value is used instead for that acceleration)
+ *
+ */
+ applyForces:function(th) {
+ th.x=toys.topview.getNextX(th);
+ th.y=toys.topview.getNextY(th);
+ },
+
+ /**
+ * This applies acceleration in the Z direction (not nessesarily gravity but whatever the next accerlation on the Z axis is)
+ * @param {Object} th The object being modified.
+ *
+ *
z: the current z position of the object
+ *
accz: the object's currect acceleration in the z direction
+ *
maxacc: the max accleration the object can have (if accz is greater than this then this value is used instead)
+ *
+ */
+ applyGravity:function(th) {
+ th.z=toys.topview.getNextZ(th);
+ },
+
+ /**
+ * Degrades all accelerations on an object by one toward zero.
+ * @param {Object} th The object being modified.
+ *
+ *
xpushing: a boolean that notes whether the object is pushing against something in the x direction.
+ *
ypushing: a boolean that notes whether the object is pushing against something in the y direction.
+ *
accx: the object's currect acceleration in the x direction
+ *
accy: the object's currect acceleration in the y direction
+ *
+ */
+ handleAccellerations:function(th) {
+ if (!th.xpushing) th.accx=help.goToZero(th.accx);
+ if (!th.ypushing) th.accy=help.goToZero(th.accy);
+
+ },
+
+ /**
+ * Increases the Z acceleration on the object by one.
+ * @param {Object} th The object being modified.
+ *
+ *
accz: the acceleration on the Z axis
+ *
+ */
+ handleGravity:function(th) {
+ th.accz++;
+ },
+
+ /**
+ * This sets which frame the object is going to display based on an agregate word that describes predefined states.
+ * @param {Object} th The object whose frame is being set.
+ *
+ *
xpushing: a boolean that notes whether the object is pushing against something in the x direction.
+ *
ypushing: a boolean that notes whether the object is pushing against something in the y direction.
+ *
haspushing: a boolean that notes if the object changes when pushing against something.
+ *
toucheddown: a boolean that notes if the object is touching something below it on the screen.
+ *
touchedup: a boolean that notes if the object is touching something above it on the screen.<
+ *
touchedright: a boolean that notes if the object is touching something right of it on the screen.<
+ *
touchedleft: a boolean that notes if the object is touching something left of it on the screen.<
+ *
flipside:
+ *
fliph:
+ *
facing:
+ *
frames:
+ *
frame:
+ *
counter:
+ *
+ * // incomplete
+ */
+ setFrame:function(th) {
+ var pref="stand";
+ if (th.xpushing||th.ypushing)
+ if (th.haspushing&&(th.toucheddown||th.touchedup||th.touchedleft||th.touchedright)) pref="pushing"; else pref="moving";
+ if (th.flipside)
+ th.fliph=(th.facing==toys.FACE_RIGHT);
+ th.frame=help.decideFrame(th.counter,th.frames[pref+toys.FACES[th.facing]]);
+ },
+
+ /**
+ * Checks if the specified object is colliding with tiles in the map in an area defined by the object's colw and colh variables as well as the tolerance and approximation variables that are passed in through data. Only tiles in the map marked as solid are checked against. The alogrithm checks the
+ * @param {Object} th The object that is being checked against the tilemap.
+ * @param {Object} map This is the asci map that the tile map is generated from.
+ * @param {Object} tilemap This is the array of tile objects that it itterated over checking for collisions.
+ * @param {Object} defaulttile The default tile to be returned if nothing can be found. Null can be used here.
+ * @param {Object} data Passes is extra dat to the function. Can be set as null.
+ *
+ *
tolerance{Integer}: This is subtracted from the collision space to get the maximum collision area for the object. This defaults to 6.
+ *
approximation{Integer}: This is the amount that the checked values are incremented by until they reach the maximum value allowed. This defaults to 10.
+ *
+ */
+ tileCollision:function(th,map,tilemap,defaulttile,data) {
+
+ th.touchedup=false;
+ th.toucheddown=false;
+ th.touchedleft=false;
+ th.touchedright=false;
+
+ var tolerance=(data&&(data.tolerance!=null)?data.tolerance:6);
+ var approximation=(data&&(data.approximation!=null)?data.approximation:10);
+ var t=tolerance-approximation;
+ do {
+ t+=approximation;
+ if (t>th.colw-tolerance-1) t=th.colw-tolerance-1;
+ var bottom=help.getTileInMap(th.x+th.colx+t,th.y+th.coly+th.colh-1,map,defaulttile,tilemap);
+ var top=help.getTileInMap(th.x+th.colx+t,th.y+th.coly,map,defaulttile,tilemap);
+ if (map.tileIsSolid(th,top)) th.touchedup=true;
+ if (map.tileIsSolid(th,bottom)) th.toucheddown=true;
+ } while (t!=th.colw-tolerance-1);
+
+ t=tolerance-approximation;
+ do {
+ t+=approximation;
+ if (t>th.colh-tolerance-1) t=th.colh-tolerance-1;
+ var left=help.getTileInMap(th.x+th.colx,th.y+th.coly+t,map,defaulttile,tilemap);
+ var right=help.getTileInMap(th.x+th.colx+th.colw-1,th.y+th.coly+t,map,defaulttile,tilemap);
+ if (map.tileIsSolid(th,left)) th.touchedleft=true;
+ if (map.tileIsSolid(th,right)) th.touchedright=true;
+ } while (t!=th.colh-tolerance-1);
+
+ if (th.touchedup) {
+ th.accy=0;
+ th.y=help.yPixelToTile(map,th.y+th.coly,1)-th.coly;
+ }
+ if (th.toucheddown) {
+ th.accy=0;
+ th.y=help.yPixelToTile(map,th.y+th.coly+th.colh-1)-th.coly-th.colh;
+ }
+ if (th.touchedleft) {
+ th.accx=0;
+ th.x=help.xPixelToTile(map,th.x+th.colx,1)-th.colx;
+ }
+ if (th.touchedright) {
+ th.accx=0;
+ th.x=help.xPixelToTile(map,th.x+th.colx+th.colw-1)-th.colx-th.colw;
+ }
+
+ },
+
+ /**
+ * @param {Object} th The object being checked for collisions.
+ *
+ *
+ *
+ *
+ *
+ *
+ * @param {Object} data This is used to pass in other data and arguments.
+ *
+ *
group {String}: (required) This is the group of objects being checked against.
+ *
+ *
+ *
+ *
+ *
//incomplete
+ */
+ spritewallCollision:function(th,data) {
+ var wl;
+ for (var i in gbox._objects[data.group])
+ if ((!gbox._objects[data.group][i].initialize)&&toys.topview.collides(th,gbox._objects[data.group][i])) {
+ wl=gbox._objects[data.group][i];
+ if (toys.topview.pixelcollides({x:th.x+th.colx,y:th.y+th.coly+th.colhh},wl)) {
+ th.touchedleft=true;
+ th.accx=0;
+ th.x=wl.x+wl.colx+wl.colw-th.colx;
+ } else if (toys.topview.pixelcollides({x:th.x+th.colx+th.colw,y:th.y+th.coly+th.colhh},wl)) {
+ th.touchedright=true;
+ th.accx=0;
+ th.x=wl.x+wl.colx-th.colw-th.colx;
+ }
+ if (toys.topview.pixelcollides({x:th.x+th.colx+th.colhw,y:th.y+th.coly+th.colh},wl)) {
+ th.toucheddown=true;
+ th.accy=0;
+ th.y=wl.y+wl.coly-th.colh-th.coly;
+ } else if (toys.topview.pixelcollides({x:th.x+th.colx+th.colhw,y:th.y+th.coly},wl)) {
+ th.touchedup=true;
+ th.accy=0;
+ th.y=wl.y+wl.coly+wl.colh-th.coly;
+ }
+ }
+
+ },
+
+ /**
+ * This checks if the object's z index is 0 which means it has hit the floor. If this has occured it also plays an impact or bounce noise if one is passed in. Note: The area above the floor is in the negative z index space so a value of 1 for z will return that the object has collided with the floor and z will then be set to zero.
+ * @param {Object} th The object being checked for collision.
+ *
+ *
touchedfloor{boolean}: This value is not passed in but is created or set in the function. This contains the function's return value.
+ *
+ *
+ *
+ *
+ * @param {Object} data This is used to pass in extra parameters.
+ *
+ *
+ *
+ */
+ floorCollision:function(th,data) {
+ th.touchedfloor=false;
+ if (th.z>0) {
+ th.accz=(data==null?0:-Math.floor(th.accz/data.bounce));
+ if (data&&data.audiobounce&&th.accz) gbox.hitAudio(data.audiobounce);
+ th.z=0;
+ th.touchedfloor=true;
+ }
+ },
+
+ /**
+ *
+ */
+ adjustZindex:function(th) {
+ gbox.setZindex(th,th.y+th.h);
+ },
+
+ /**
+ *
+ */
+ // Helper: returns the ahead pixel (i.e. destination use action)
+ getAheadPixel:function(th,data) {
+ switch (th.facing) {
+ case toys.FACE_RIGHT:{
+ return {x:th.x+th.colx+th.colw+data.distance,y:th.y+th.coly+th.colhh};
+ break;
+ }
+ case toys.FACE_LEFT:{
+ return {x:th.x+th.colx-data.distance,y:th.y+th.coly+th.colhh};
+ break;
+ }
+ case toys.FACE_UP:{
+ return {x:th.x+th.colx+th.colhw,y:th.y+th.coly-data.distance};
+ break;
+ }
+ case toys.FACE_DOWN:{
+ return {x:th.x+th.colx+th.colhw,y:th.y+th.coly+th.colh+data.distance};
+ break;
+ }
+ }
+ },
+
+ /**
+ *
+ */
+ // Helper: trigger a method in colliding objects (i.e. "use action")
+ callInColliding:function(th,data) {
+ for (var i in gbox._objects[data.group])
+ if ((!gbox._objects[data.group][i].initialize)&&toys.topview.pixelcollides(data,gbox._objects[data.group][i]))
+ if (gbox._objects[data.group][i][data.call]) {
+ gbox._objects[data.group][i][data.call](th);
+ return i;
+ }
+ return false;
+ },
+
+ /**
+ *
+ */
+ // Enemy methods
+ wander:function(th,map,tilemap,defaulttile,data) {
+ if ((!th.wandercounter)||(th.toucheddown||th.touchedup||th.touchedleft||th.touchedright)) {
+ th.wandercounter=help.random(data.minstep,data.steprange);
+ th.wanderdirection=help.random(0,4);
+ } else th.wandercounter--;
+ switch (th.wanderdirection) {
+ case toys.FACE_LEFT: {
+ th.xpushing=toys.PUSH_LEFT;
+ th.ypushing=toys.PUSH_NONE;
+ th.facing=toys.FACE_LEFT;
+ th.accx=-data.speed;
+ th.accy=0;
+ break;
+ }
+ case toys.FACE_RIGHT: {
+ th.xpushing=toys.PUSH_RIGHT;
+ th.ypushing=toys.PUSH_NONE;
+ th.facing=toys.FACE_RIGHT;
+ th.accx=data.speed;
+ th.accy=0;
+ break;
+ }
+ case toys.FACE_UP: {
+ th.ypushing=toys.PUSH_UP;
+ th.xpushing=toys.PUSH_NONE;
+ th.facing=toys.FACE_UP;
+ th.accy=-data.speed;
+ th.accx=0;
+ break;
+ }
+ case toys.FACE_DOWN: {
+ th.ypushing=toys.PUSH_DOWN;
+ th.xpushing=toys.PUSH_NONE;
+ th.facing=toys.FACE_DOWN;
+ th.accy=data.speed;
+ th.accx=0;
+ break;
+ }
+ }
+ },
+
+ /**
+ *
+ */
+ // generators (firebullet specific for topdown - more complex than SHMUP one)
+ fireBullet:function(gr,id,data) {
+
+ var ts=gbox.getTiles(data.tileset);
+
+
+ var obj=gbox.addObject(
+ help.mergeWithModel(
+ data,{
+ _bullet:true,
+ zindex:0,
+ fliph:false, flipv:false,
+ id:id,
+ group:gr,
+ cnt:0,
+ tileset:"",
+ frames:{},
+ acc:0,
+ angle:0,
+ camera:data.from.camera,
+ accx:(data.accx==null?Math.floor(trigo.translateX(0,data.angle,data.acc)):0),
+ accy:(data.accy==null?Math.floor(trigo.translateY(0,data.angle,data.acc)):0),
+ accz:0,
+ x:(data.sidex==toys.FACE_LEFT?data.from.x-ts.tilehw:(data.sidex==toys.FACE_RIGHT?data.from.x+data.from.w-ts.tilehw:data.from.x+data.from.hw-ts.tilehw))+(data.gapx?data.gapx:0),
+ y:(data.sidey==toys.FACE_UP?data.from.y-ts.tilehh:(data.sidey==toys.FACE_DOWN?data.from.y+data.from.h-ts.tilehh:data.from.y+data.from.hh-ts.tilehh))+(data.gapy?data.gapy:0),
+ z:(data.from.z==null?0:data.from.z),
+ collidegroup:"",
+ spark:toys.NOOP,
+ power:1,
+ lifetime:null,
+ tilemap:null,
+ defaulttile:0,
+ applyzgravity:false,
+ map:null,
+ defaulttile:0,
+ mapindex:"",
+ spritewalls:null,
+ colx:(data.fullhit?0:null),
+ coly:(data.fullhit?0:null),
+ colh:(data.fullhit?ts.tileh:null),
+ colw:(data.fullhit?ts.tilew:null),
+ duration:null,
+ onWallHit:function() {
+ this.spark(this);
+ gbox.trashObject(this);
+ },
+ bulletIsAlive:function() {
+ return gbox.objectIsVisible(this);
+ }
+ }
+ )
+ );
+
+ obj.initialize=function() {
+ toys.topview.initialize(this);
+ };
+
+ obj[(data.logicon==null?"first":data.logicon)]=function() {
+ this.cnt=(this.cnt+1)%10;
+
+ if (this.applyzgravity) toys.topview.handleGravity(this); // z-gravity
+ toys.topview.applyForces(this); // Apply forces
+ if (this.applyzgravity) toys.topview.applyGravity(this); // z-gravity
+ if (this.map!=null) toys.topview.tileCollision(this,this.map,this.mapindex,this.defaulttile); // tile collisions
+ if (this.spritewalls!=null) toys.topview.spritewallCollision(this,{group:this.spritewalls}); // walls collisions
+ if (this.applyzgravity) toys.topview.floorCollision(this); // Collision with the floor (for z-gravity)
+ toys.topview.adjustZindex(this);
+ if (this.duration!=null) {
+ this.duration--;
+ if (this.duration==0) gbox.trashObject(this);
+ }
+ if (!this.bulletIsAlive()) gbox.trashObject(this);
+ else if (this.toucheddown||this.touchedup||this.touchedleft||this.touchedright) this.onWallHit();
+ else if (this.collidegroup!=null)
+ for (var i in gbox._objects[this.collidegroup])
+ if ((!gbox._objects[this.collidegroup][i].initialize)&&toys.topview.collides(this,gbox._objects[this.collidegroup][i],gbox._objects[this.collidegroup][i].tolerance)) {
+ if (gbox._objects[this.collidegroup][i]["hitByBullet"]!=null)
+ if (!gbox._objects[this.collidegroup][i].hitByBullet(this)) {
+ this.spark(this);
+ gbox.trashObject(this);
+ }
+ }
+ }
+
+ obj[(data.bliton==null?"blit":data.bliton)]=function() {
+ if (!gbox.objectIsTrash(this))
+ gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.cnt,this.frames),dx:this.x,dy:this.y+this.z,camera:this.camera,fliph:this.fliph,flipv:this.flipv});
+ }
+
+ gbox.setZindex(obj,obj.y+obj.h);
+
+ return obj;
+
+ },
+
+ /**
+ *
+ */
+ makedoor:function(gr,id,map,data) {
+
+ var mts=gbox.getTiles(map.tileset);
+ var ts=gbox.getTiles(data.tileset);
+
+ var obj=gbox.addObject(
+ help.mergeWithModel(
+ data,{
+ zindex:0,
+ fliph:false, flipv:false,
+ id:id,
+ group:gr,
+ cnt:0,
+ tileset:"",
+ frames:{},
+ camera:true,
+ x:data.tilex*mts.tilew,
+ y:data.tiley*mts.tileh,
+ z:0,
+ tilemap:null,
+ defaulttile:0,
+ map:map,
+ colx:(data.fullhit?0:null),
+ coly:(data.fullhit?0:null),
+ colh:(data.fullhit?ts.tileh:null),
+ colw:(data.fullhit?ts.tilew:null),
+ opening:false,
+ doorheight:ts.tileh,
+ opencounter:0,
+ opening:false,
+ closing:false,
+ audiobefore:null,
+ audioafter:null,
+ doOpen:function() {
+ this.opening=true;
+ },
+ whenClosed:toys.NOOP,
+ whenOpened:toys.NOOP,
+ whileMoving:toys.NOOP,
+ hitByBullet:function(by) {
+
+ }
+ }
+ )
+ );
+
+ // Closing animation
+ if (obj.closing) obj.opencounter=obj.doorheight;
+
+ obj.initialize=function() {
+ this.ismoving=false;
+ toys.topview.initialize(this);
+ };
+
+ obj[(data.logicon==null?"first":data.logicon)]=function() {
+ if (this.closing) {
+ if (!this.ismoving) {
+ if (this.audiobefore) gbox.hitAudio(this.audiobefore);
+ this.ismoving=true;
+ }
+ this.whileMoving();
+ this.opencounter--;
+ if (this.opencounter<0) {
+ if (this.audioafter) gbox.hitAudio(this.audioafter);
+ this.ismoving=false;
+ this.opencounter=0;
+ this.closing=false;
+ this.whenClosed();
+ }
+ }
+ if (this.opening) {
+ if (!this.ismoving) {
+ if (this.audiobefore) gbox.hitAudio(this.audiobefore);
+ this.ismoving=true;
+ }
+ this.whileMoving();
+ this.opencounter++;
+ if (this.opencounter>=this.doorheight) {
+ if (this.audioafter) gbox.hitAudio(this.audioafter);
+ this.ismoving=false;
+ if (!this.whenOpened()) gbox.trashObject(this);
+ }
+ }
+ }
+
+ obj[(data.bliton==null?"blit":data.bliton)]=function() {
+ if (!gbox.objectIsTrash(this))
+ gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.cnt,this.frames),dx:this.x,dy:this.y+this.z+this.opencounter,h:this.h-this.opencounter,camera:this.camera,fliph:this.fliph,flipv:this.flipv});
+ }
+
+ gbox.setZindex(obj,obj.y+obj.h);
+
+ return obj;
+ },
+ // Set the object speed making sure that the X and Y coords are multiple of the speed. Useful on maze-based games.
+ setStaticSpeed:function(th,speed) {
+ th.staticspeed=speed;
+ th.x=Math.round(th.x/speed)*speed;
+ th.y=Math.round(th.y/speed)*speed;
+ }
+ },
+
+
+ /**
+ * @namespace shmup The libraries for a 2D top-down Shmup game.
+ */
+ // Shoot'em up specifics
+ shmup:{
+
+ /**
+ *
+ */
+ initialize:function(th,data) {
+ help.mergeWithModel(
+ th,
+ help.mergeWithModel(
+ data,
+ {
+ x:0, y:0,
+ accx:0, accy:0,
+ frames:{},
+ maxacc:5, controlmaxacc:5,
+ responsive:0, // Responsiveness
+ bounds:{x:0,y:0,w:gbox.getScreenW(),h:gbox.getScreenH()}, // Bounds box (ship cannot exit from there)
+ weapon:0, // Weapon
+ hittime:5,
+ camera:false,
+ flipv:false, fliph:false,
+ health:1,
+ tolerance:4
+ }
+ )
+ );
+ toys.shmup.spawn(th);
+ },
+
+ /**
+ *
+ */
+ spawn:function(th,data) {
+ th.xpushing=toys.PUSH_NONE; // user is moving side
+ th.vpushing=toys.PUSH_NONE; // user is moving side
+ th.counter=0; // self counter
+ th.hittimer=0;
+ th.killed=false;
+ help.copyModel(th,data);
+ },
+
+ /**
+ *
+ */
+ getNextX:function(th) { return th.x+help.limit(th.accx,-th.maxacc,th.maxacc); },
+
+ /**
+ *
+ */
+ getNextY:function(th) { return th.y+help.limit(th.accy,-th.maxacc,th.maxacc); },
+
+ /**
+ *
+ */
+ controlKeys:function(th,keys) {
+
+ if (gbox.keyIsPressed(keys.left)) {
+ th.xpushing=toys.PUSH_LEFT;
+ if (th.accx>th.responsive) th.accx=th.responsive;
+ th.accx=help.limit(th.accx-1,-th.controlmaxacc,th.controlmaxacc);
+ } else if (gbox.keyIsPressed(keys.right)) {
+ th.xpushing=toys.PUSH_RIGHT;
+ if (th.accx<-th.responsive) th.accx=-th.responsive;
+ th.accx=help.limit(th.accx+1,-th.controlmaxacc,th.controlmaxacc);
+ } else th.xpushing=toys.PUSH_NONE;
+ if (gbox.keyIsPressed(keys.up)) {
+ th.ypushing=toys.PUSH_UP;
+ if (th.accy>th.responsive) th.accy=th.responsive;
+ th.accy=help.limit(th.accy-1,-th.controlmaxacc,th.controlmaxacc);
+ } else if (gbox.keyIsPressed(keys.down)) {
+ th.ypushing=toys.PUSH_DOWN;
+ if (th.accy<-th.responsive) th.accy=-th.responsive;
+ th.accy=help.limit(th.accy+1,-th.controlmaxacc,th.controlmaxacc);
+ } else th.ypushing=toys.PUSH_NONE;
+ },
+
+ /**
+ *
+ */
+ applyForces:function(th) {
+ th.x=toys.shmup.getNextX(th);
+ th.y=toys.shmup.getNextY(th);
+ },
+
+ /**
+ *
+ */
+ handleAccellerations:function(th) {
+ if (!th.xpushing) th.accx=help.goToZero(th.accx);
+ if (!th.ypushing) th.accy=help.goToZero(th.accy);
+ },
+
+ /**
+ *
+ */
+ keepInBounds:function(th) {
+ if (th.xth.bounds.x+th.bounds.w) {
+ th.x=th.bounds.x+th.bounds.w-th.w;
+ th.accx=0;
+ }
+ if (th.yth.bounds.y+th.bounds.h) {
+ th.y=th.bounds.y+th.bounds.h-th.h;
+ th.accy=0;
+ }
+ },
+
+ /**
+ *
+ */
+ setFrame:function(th) {
+ if (th.hittimer) th.hittimer--;
+ th.frame=help.decideFrame(th.counter,(th.hittimer?th.frames.hit:th.frames.still));
+ },
+
+ /**
+ *
+ */
+ fireBullet:function(gr,id,data) {
+
+ var ts=gbox.getTiles(data.tileset);
+
+ var obj=gbox.addObject(
+ help.mergeWithModel(
+ data,{
+ _bullet:true,
+ fliph:false, flipv:false,
+ id:id,
+ group:gr,
+ cnt:0,
+ tileset:"",
+ frames:{},
+ acc:0,
+ angle:0,
+ camera:false,
+ accx:(data.accx==null?Math.floor(trigo.translateX(0,data.angle,data.acc)):0),
+ accy:(data.accy==null?Math.floor(trigo.translateY(0,data.angle,data.acc)):0),
+ x:data.from.x+data.from.hw-ts.tilehw+(data.gapx?data.gapx:0),
+ y:(data.upper?data.from.y-ts.tilehh+(data.gapy?data.gapy:0):data.from.y+data.from.h-ts.tilehh-(data.gapy?data.gapy:0)),
+ collidegroup:"",
+ spark:toys.NOOP,
+ power:1
+ }
+ )
+ );
+
+ obj[(data.logicon==null?"first":data.logicon)]=function() {
+ this.x+=this.accx;
+ this.y+=this.accy;
+ this.cnt=(this.cnt+1)%10;
+ if (!gbox.objectIsVisible(this)) gbox.trashObject(this);
+ else if (this.collidegroup!=null)
+ for (var i in gbox._objects[this.collidegroup])
+ if ((!gbox._objects[this.collidegroup][i].initialize)&&gbox.collides(this,gbox._objects[this.collidegroup][i],gbox._objects[this.collidegroup][i].tolerance)) {
+ if (gbox._objects[this.collidegroup][i]["hitByBullet"]!=null)
+ if (!gbox._objects[this.collidegroup][i].hitByBullet(this)) {
+ this.spark(this);
+ gbox.trashObject(this);
+ }
+ }
+ }
+
+ obj[(data.bliton==null?"blit":data.bliton)]=function() {
+ gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.cnt,this.frames),dx:this.x,dy:this.y,camera:this.camera,fliph:this.side,flipv:this.flipv});
+ }
+
+ return obj;
+
+ },
+
+ /**
+ *
+ */
+ hitByBullet:function(th,by) {
+ if (by.power) {
+ th.health-=by.power;
+ if (th.health<=0) th.kill(by); else th.hittimer=th.hittime;
+ }
+ },
+
+ /**
+ *
+ */
+ generateEnemy:function(gr,id,data,model) {
+ help.mergeWithModel(data,model);
+ var obj=gbox.addObject(
+ help.mergeWithModel(
+ data,{
+ id:id,
+ group:gr,
+ cnt:0,
+ tileset:"",
+ frames:{},
+ acc:0,
+ angle:0,
+ camera:false,
+ fliph:false,
+ flipv:false,
+ accx:(data.accx==null?Math.floor(trigo.translateX(0,data.angle,data.acc)):0),
+ accy:(data.accy==null?Math.floor(trigo.translateY(0,data.angle,data.acc)):0),
+ x:data.x,
+ y:data.y,
+ // -- spec
+ animationset:"still",
+ defaultanimationset:"still",
+ hitanimationset:"still",
+ hittime:5,
+ script:toys.NOOP,
+ handler:toys.NOOP,
+ scriptline:(data.scriptline==null?-1:data.scriptline-1),
+ newline:true,
+ waitframes:0,
+ doframes:0,
+ mode:0,
+ line:{},
+ dohandler:null,
+ ended:false,
+ health:1,
+ hittimer:0,
+ kill:toys.NOOP,
+ tolerance:0,
+ initialize:null,
+ invulnerable:false,
+ hitAnimation:function(time) {
+ this.hittimer=(time==null?this.hittime:time);
+ this.animationset=this.hitanimationset;
+ },
+ goTo:function(nl) { // Jump to a line
+ this.waitframes=0;
+ this.doframes=0;
+ this.line={};
+ this.scriptline=nl-1;
+ },
+ hitByBullet:function(by) {
+ if (!this.invulnerable&&by.power) {
+ this.health-=by.power;
+ if (this.health<=0) this.kill(this,by); else this.hitAnimation();
+ }
+ }
+ }
+ )
+ );
+
+
+ obj[(data.logicon==null?"first":data.logicon)]=function() {
+ if (this.initialize!=null) {
+ this.initialize(this);
+ this.initialize=null;
+ }
+ if (!this.ended) {
+ if (!this.waitframes&&!this.doframes&&((this.line.waitfor==null)||this.line.waitfor(this))) {
+ this.scriptline++;
+ this.everycnt=-1;
+ if (this.script[this.scriptline]==null)
+ this.ended=true;
+ else {
+ if (this.script[this.scriptline]["goto"]!=null) this.scriptline=this.script[this.scriptline]["goto"];
+ this.line=this.script[this.scriptline];
+ if (this.line.afterframes!=null)
+ this.waitframes=this.line.afterframes;
+ if (this.line.forframes!=null)
+ this.doframes=this.line.forframes;
+ else
+ this.doframes=1;
+ if (this.line.cleardo)
+ this.dohandler=null;
+ else if (this.line.doit!=null) {
+ this.dohandler={
+ actiontimes:0,
+ timer:(this.line.doit.every=="keep"?this.dohandler.every:this.line.doit.every),
+ every:(this.line.doit.every=="keep"?this.dohandler.every:this.line.doit.every),
+ once:(this.line.doit.once=="keep"?this.dohandler.once:this.line.doit.once),
+ action:(this.line.doit.action=="keep"?this.dohandler.action:this.line.doit.action),
+ render:(this.line.doit.render=="keep"?this.dohandler.render:this.line.doit.render)
+ }
+ }
+
+ }
+ }
+ if (!this.waitframes&&this.doframes&&!this.ended) {
+ this.doframes--;
+ if (this.line.setinvulnerable!=null) this.invulnerable=this.line.setinvulnerable;
+ if (this.line.setx!=null) this.x=this.line.setx;
+ if (this.line.sety!=null) this.y=this.line.sety;
+ if (this.line.addx!=null) this.x+=this.line.addx;
+ if (this.line.addy!=null) this.y+=this.line.addy;
+ if (this.line.setaccx!=null) this.accx=this.line.setaccx;
+ if (this.line.setaccy!=null) this.accy=this.line.setaccy;
+ if (this.line.setacc!=null) {
+ this.acc=this.line.setacc;
+ this.accx=Math.floor(trigo.translateX(0,this.angle,this.acc));
+ this.accy=Math.floor(trigo.translateY(0,this.angle,this.acc));
+ }
+ if (this.line.addaccx!=null) this.accx+=this.line.addaccx;
+ if (this.line.addaccy!=null) this.accy+=this.line.addaccy;
+ if (this.line.addacc!=null) {
+ this.acc+=this.line.addacc;
+ this.accx=Math.floor(trigo.translateX(0,this.angle,this.acc));
+ this.accy=Math.floor(trigo.translateY(0,this.angle,this.acc));
+ }
+
+ if (this.line.setangle!=null) {
+ this.angle=this.line.setangle;
+ this.accx=Math.floor(trigo.translateX(0,this.angle,this.acc));
+ this.accy=Math.floor(trigo.translateY(0,this.angle,this.acc));
+ }
+ if (this.line.addangle!=null) {
+ this.angle+=this.line.addangle;
+ this.accx=Math.floor(trigo.translateX(0,this.angle,this.acc));
+ this.accy=Math.floor(trigo.translateY(0,this.angle,this.acc));
+ }
+ if (this.line.everyframe) this.waitframes=this.line.everyframe;
+
+ }
+ if (this.waitframes>0) this.waitframes--;
+ }
+ if (this.dohandler&&(this.dohandler.action!=null)) {
+ if (this.dohandler.timer==this.dohandler.every) {
+ this.dohandler.action(this,this.dohandler.actiontimes);
+ this.dohandler.timer=0;
+ this.dohandler.actiontimes++;
+ } else if (!this.dohandler.once) this.dohandler.timer++;
+ }
+ if (this.handler!=null) this.handler(this);
+
+ if (this.hittimer) {
+ this.hittimer--;
+ if (!this.hittimer) this.animationset=this.defaultanimationset;
+ }
+
+ this.x+=this.accx;
+ this.y+=this.accy;
+ this.cnt=(this.cnt+1)%10;
+
+ }
+
+ obj[(data.bliton==null?"blit":data.bliton)]=function() {
+ gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.cnt,this.frames[this.animationset]),dx:this.x,dy:this.y,camera:this.camera,fliph:this.side,flipv:this.flipv});
+ if (this.dohandler&&(this.dohandler.render!=null)) this.dohandler.render(this);
+ }
+
+ return obj;
+
+ },
+
+ /**
+ *
+ */
+ generateScroller:function(gr,id,data) {
+ var obj=gbox.addObject(
+ help.mergeWithModel(
+ help.cloneObject(data),{
+ id:id, group:gr,
+ y:0, x:0,
+ stage:{},
+ speed:0,
+ stop:null, // Remember to set the last stop ever! or the last loop!
+ block:-1,
+ bly:0,
+ lblock:-1,
+ lbly:0,
+ lget:0,
+ tbly:0,
+ trb:0,
+ maxwidth:0,
+ loopstart:null, loopend:null, looprounds:0,
+ panspeed:1, panstimer:0, destspeed:0,
+
+ setLoop:function(st,en) {
+ this.loopstart=st;
+ this.loopend=en;
+ this.lget=1;
+ this.looprounds=1;
+ },
+
+ quitLoop:function() {
+ this.setLoop(null,null);
+ this.looprounds=0;
+ },
+
+ setSpeed:function(s) {
+ this.speed=s;
+ this.destspeed=s;
+ },
+
+ panToSpeed:function(s,pans) {
+ this.destspeed=s;
+ this.panspeed=pans;
+ },
+
+ quitStop:function() {
+ this.stop=null;
+ },
+
+ setStop:function(s) {
+ this.stop=s;
+ },
+
+ setX:function(x) {
+ if (x<0) this.x=0; else
+ if (x+gbox.getScreenW()>this.maxwidth) this.x=this.maxwidth-gbox.getScreenW();
+ else this.x=x;
+ }
+
+ }
+ )
+ );
+
+ obj[(data.logicon==null?"first":data.logicon)]=function() {
+ if ((this.stop==null)||(this.ythis.destspeed) this.speed--;
+ this.panstimer=this.panspeed;
+ }
+ }
+ this.y+=this.speed;
+ if ((this.stop!=null)&&(this.y>=this.stop)) this.y=this.stop;
+ if ((this.loopend!=null)&&(this.y>this.loopend)) {
+ this.y=this.loopstart+(this.y-this.loopend);
+ this.looprounds++;
+ if (this.lget==1) {
+ this.block=0;
+ this.bly=0;
+ this.lget=2;
+ } else {
+ this.block=this.lblock;
+ this.bly=this.lbly;
+ }
+
+ }
+ }
+
+ // Cerca il blocco da mostrare
+ this.trb=this.block;
+ this.tbly=this.bly;
+ do {
+ this.trb++;
+ this.tbly+=gbox.getImage(this.stage[this.trb].image).height;
+ } while (this.tblygbox.getScreenH()) done=true;
+ gbox.blitAll(gbox.getBufferContext(),gbox.getImage(this.stage[this.trb].image),{dx:-this.x,dy:gbox.getScreenH()-dy});
+ this.trb++;
+ dy+=gbox.getImage(this.stage[this.trb].image).height;
+ } while (!done);
+ }
+
+ return obj;
+ }
+ },
+
+ /**
+ * @namespace platformer The libraries for generating a 2D platformer game.
+ */
+ platformer:{
+
+ /**
+ *
+ */
+ initialize:function(th,data) {
+ help.mergeWithModel(
+ th,
+ help.mergeWithModel(
+ data,
+ {
+ maxaccx:5, maxaccy:10,
+ jumpsize:6, jumpaccy:6,
+ accx:0, accy:0,
+ x:0, y:0,
+ frames:{},
+ camera:true,
+ flipv:false,
+ side:false
+ }
+ )
+ );
+ toys.platformer.spawn(th);
+ },
+
+ /**
+ *
+ */
+ spawn:function(th,data) {
+ th.curjsize=0; // current jump size
+ th.counter=0; // self counter
+ th.touchedfloor=false; // touched floor
+ th.touchedceil=false;
+ th.touchedleftwall=false;
+ th.touchedrightwall=false;
+ th.pushing=toys.PUSH_NONE; // user is moving side
+ th.killed=false;
+ help.copyModel(th,data);
+ },
+
+ /**
+ *
+ */
+ getNextX:function(th) { return th.x+th.accx; },
+
+ /**
+ *
+ */
+ getNextY:function(th) { return th.y+help.limit(th.accy,-th.maxaccy,th.maxaccy); },
+
+ /**
+ *
+ */
+ applyGravity:function(th) {
+ th.x=toys.platformer.getNextX(th);
+ th.y=toys.platformer.getNextY(th);
+ },
+
+ /**
+ *
+ */
+ horizontalKeys:function(th,keys) {
+ if (gbox.keyIsPressed(keys.left)) {
+ th.pushing=toys.PUSH_LEFT;
+ th.accx=help.limit(th.accx-1,-th.maxaccx,th.maxaccx);
+ } else if (gbox.keyIsPressed(keys.right)) {
+ th.pushing=toys.PUSH_RIGHT;
+ th.accx=help.limit(th.accx+1,-th.maxaccx,th.maxaccx);
+ } else th.pushing=toys.PUSH_NONE;
+ },
+
+ /**
+ *
+ */
+ verticalTileCollision:function(th,map,tilemap) {
+ var bottom=help.getTileInMap(th.x+(th.w/2),th.y+th.h,map,0,tilemap);
+ var top=help.getTileInMap(th.x+(th.w/2),th.y,map,0,tilemap);
+ th.touchedfloor=false;
+ th.touchedceil=false;
+
+ if (map.tileIsSolidCeil(th,top)) {
+ th.accy=0;
+ th.y=help.yPixelToTile(map,th.y,1);
+ th.touchedceil=true;
+ }
+ if (map.tileIsSolidFloor(th,bottom)) {
+ th.accy=0;
+ th.y=help.yPixelToTile(map,th.y+th.h)-th.h;
+ th.touchedfloor=true;
+ }
+ },
+
+ /**
+ *
+ */
+ horizontalTileCollision:function(th,map,tilemap,precision) {
+ var left=0;
+ var right=0;
+ var t=0;
+
+ th.touchedleftwall=false;
+ th.touchedrightwall=false;
+
+ while (t0)&&map.tileIsSolidFloor(th,right)) {
+ th.accx=0;
+ th.x=help.xPixelToTile(map,th.x+th.w)-th.w;
+ th.touchedrightwall=true;
+ }
+ t+=gbox.getTiles(map.tileset).tileh/(precision?precision:1);
+ }
+ },
+
+ /**
+ * Checks if the passed object is touching the floor and can therefore jump at present.
+ * @param th This is the object being checked for jump ability at the time of calling.
+ */
+ canJump:function(th) {
+ return th.touchedfloor;
+ },
+
+ /**
+ *
+ */
+ jumpKeys:function(th,key) {
+ if ((toys.platformer.canJump(th)||(key.doublejump&&(th.accy>=0)))&&gbox.keyIsHit(key.jump)&&(th.curjsize==0)) {
+ if (key.audiojump) gbox.hitAudio(key.audiojump);
+ th.accy=-th.jumpaccy;
+ th.curjsize=th.jumpsize;
+ return true;
+ } else if (th.curjsize&&gbox.keyIsHold(key.jump)) { // Jump modulation
+ th.accy--;
+ th.curjsize--;
+ } else
+ th.curjsize=0;
+ return false;
+ },
+
+ /**
+ *
+ */
+ bounce:function(th,data) {
+ th.curjsize=0;
+ th.accy=-data.jumpsize;
+ },
+
+ /**
+ *
+ */
+ handleAccellerations:function(th) {
+ // Gravity
+ if (!th.touchedfloor) th.accy++;
+ // Attrito
+ if (th.pushing==toys.PUSH_NONE) if (th.accx) th.accx=help.goToZero(th.accx);
+ },
+
+ /**
+ *
+ */
+ setSide:function(th) {
+ if (th.accx) th.side=th.accx>0;
+ },
+
+ /**
+ *
+ */
+ setFrame:function(th) {
+ if (th.touchedfloor)
+ if (th.pushing!=toys.PUSH_NONE)
+ th.frame=help.decideFrame(th.counter,th.frames.walking);
+ else
+ th.frame=help.decideFrame(th.counter,th.frames.still);
+ else if (th.accy>0)
+ th.frame=help.decideFrame(th.counter,th.frames.falling);
+ else
+ th.frame=help.decideFrame(th.counter,th.frames.jumping);
+ },
+
+ /**
+ *
+ */
+ auto:{
+ // Moves on a platform. It tries to do not fall down, if specified.
+ // Args: (object,{moveWhileFalling:,speed:})
+ // Outs: the frame
+ goomba:function(th,data) {
+ if (data.moveWhileFalling||th.touchedfloor) {
+ if (th.side) {
+ th.pushing=toys.PUSH_LEFT;
+ th.accx=-data.speed;
+ } else {
+ th.pushing=toys.PUSH_RIGHT;
+ th.accx=data.speed;
+ }
+ } else th.pushing=toys.PUSH_NONE;
+ },
+ dontFall:function(th,map,tilemap) {
+ if (th.accx&&th.touchedfloor) {
+ var til;
+ if (th.accx>0) til=help.getTileInMap(toys.platformer.getNextX(th)+th.w-1+th.accx,th.y+th.h,map,0,tilemap);
+ else til=help.getTileInMap(toys.platformer.getNextX(th),th.y+th.h,map,0,tilemap);
+ if (!map.tileIsSolidFloor(th,til)) {
+ th.side=!th.side;
+ th.accx=0;
+ }
+ }
+ },
+ horizontalBounce:function(th) {
+ if (th.touchedleftwall||th.touchedrightwall) th.side=!th.side;
+ }
+ }
+ },
+
+ // State-based toys
+ // CONSTANTS
+ TOY_BUSY:0,
+ TOY_DONE:1,
+ TOY_IDLE:2,
+
+ // PRIVATE
+
+ // Generical toys method
+
+ /**
+ *
+ */
+ resetToy:function(th,id) { if (th.toys) delete th.toys[id] },
+
+ /**
+ *
+ */
+ getToyValue:function(th,id,v,def) { return ((th.toys==null)||(th.toys[id]==null)?def:th.toys[id][v]) },
+
+ /**
+ *
+ */
+ getToyStatus:function(th,id) { return ((th.toys==null)||(th.toys[id]==null)?toys.TOY_BUSY:th.toys[id].__status) },
+
+ /**
+ *
+ */
+ _toydone:function(th,id) {
+ if (th.toys[id].__statusw) w=opt.items[i].length;
+ gbox.createCanvas("menu-"+id,{w:w*fd.tilew,h:opt.items.length*fd.tileh});
+ for (var i=0;i0)) {
+ if (opt.audiooption) gbox.hitAudio(opt.audiooption);
+ th.toys[id].selected--;
+ } else
+ if (gbox.keyIsHit(opt.keys.down)&&(th.toys[id].selected0)
+ if (th.toys[id].ok<10) {
+ th.toys[id].ok++;
+ toys._toybusy(th,id);
+ } else return toys._toydone(th,id); // selected > 0
+ else return toys._toydone(th,id); // selected == -1
+ } else return toys._toybusy(th,id);
+ },
+
+ /**
+ *
+ */
+ // Returns a full customizable object for optimized huds
+ hud:function(id) {
+ gbox.createCanvas(id);
+ return {
+ w:{},
+ surfaceid:id,
+
+ /**
+ *
+ */
+ updateWidget:function(i){
+ if (!this.w[i].__hidden) {
+ if (this.w[i].widget=="label") {
+ if (this.w[i].prepad!=null) this.w[i].text=help.prepad(this.w[i].value,this.w[i].prepad,this.w[i].padwith); else
+ if (this.w[i].postpad!=null) this.w[i].text=help.postpad(this.w[i].value,this.w[i].postpad,this.w[i].padwith); else
+ this.w[i].text=this.w[i].value+"";
+ gbox.blitText(gbox.getCanvasContext(this.surfaceid),this.w[i]);
+ }
+ if (this.w[i].widget=="symbols") {
+ var ts=gbox.getTiles(this.w[i].tileset);
+ gbox.blitClear(gbox.getCanvasContext(this.surfaceid),{x:this.w[i].dx,y:this.w[i].dy,w:((this.w[i].maxshown-1)*this.w[i].gapx)+ts.tilew,h:((this.w[i].maxshown-1)*this.w[i].gapy)+ts.tileh});
+ var cnt=this.w[i].value;
+ for (var x=0;x0) {
+ gbox.blitTile(gbox.getCanvasContext(this.surfaceid),{tileset:this.w[i].tileset,tile:this.w[i].tiles[(cnt>this.w[i].tiles.length?this.w[i].tiles.length-1:cnt-1)],dx:this.w[i].dx+(x*this.w[i].gapx),dy:this.w[i].dy+(x*this.w[i].gapy),fliph:this.w[i].fliph,flipv:this.w[i].flipv});
+ } else
+ if (this.w[i].emptytile!=null)
+ gbox.blitTile(gbox.getCanvasContext(this.surfaceid),{tileset:this.w[i].tileset,tile:this.w[i].emptytile,dx:this.w[i].dx+(x*this.w[i].gapx),dy:this.w[i].dy+(x*this.w[i].gapy),fliph:this.w[i].fliph,flipv:this.w[i].flipv});
+ cnt-=this.w[i].tiles.length;
+ }
+
+ }
+ if (this.w[i].widget=="stack") {
+ var ts=gbox.getTiles(this.w[i].tileset);
+ var bw=((this.w[i].maxshown-1)*this.w[i].gapx)+ts.tilew;
+ gbox.blitClear(gbox.getCanvasContext(this.surfaceid),{x:this.w[i].dx-(this.w[i].rightalign?bw:0),y:this.w[i].dy,w:bw,h:((this.w[i].maxshown-1)*this.w[i].gapy)+ts.tileh});
+ for (var x=0;xthis.w[w].maxvalue)) v=this.w[w].maxvalue;
+ if ((this.w[w].minvalue!=null)&&(v1) th.toys[id].fade=1;
+ data.alpha=th.toys[id].fade;
+ gbox.blitFade(tox,data);
+ if (data.audiofade) gbox.setAudioVolume(data.audiofade,th.toys[id].stv*(1-data.alpha));
+ if (data.audiochannelfade)
+ if (data.alpha==1)
+ gbox.stopChannel(data.audiochannelfade);
+ else
+ gbox.setChannelVolume(data.audiochannelfade,th.toys[id].chv*(1-data.alpha));
+ return toys._toyfrombool(th,id,th.toys[id].fade==1)
+ },
+
+ /**
+ *
+ */
+ fadein:function(th,id,tox,data) {
+ if (toys._maketoy(th,id)||data.resetfade) {
+ th.toys[id].fade=1;
+ if (data.audiofade) th.toys[id].stv=gbox.getAudioVolume(data.audiofade);
+ if (data.audiochannelfade) th.toys[id].chv=gbox.getChannelDefaultVolume(data.audiochannelfade);
+ }
+ th.toys[id].fade-=data.fadespeed;
+ if (th.toys[id].fade<0) th.toys[id].fade=0;
+ if (th.toys[id].fade) {
+ data.alpha=th.toys[id].fade;
+ if (data.audiofade) gbox.setAudioVolume(data.audiofade,th.toys[id].stv*(1-data.alpha));
+ if (data.audiochannelfade) gbox.setChannelVolume(data.audiochannelfade,th.toys[id].chv*(1-data.alpha));
+ gbox.blitFade(tox,data);
+ }
+ return toys._toyfrombool(th,id,th.toys[id].fade==0);
+ }
+ },
+
+ /**
+ *
+ */
+ text:{
+
+ /**
+ *
+ */
+ blink:function(th,id,tox,data) {
+ if (toys._maketoy(th,id)) {
+ th.toys[id].texttimer=0;
+ th.toys[id].visible=false;
+ th.toys[id].times=0;
+ }
+ if (th.toys[id].texttimer>=data.blinkspeed) {
+ th.toys[id].texttimer=0;
+ th.toys[id].visible=!th.toys[id].visible;
+ if (data.times) th.toys[id].times++;
+ } else th.toys[id].texttimer++;
+ if (th.toys[id].visible)
+ gbox.blitText(tox,data);
+ return toys._toyfrombool(th,id,(data.times?data.timesth.toys[id].lh) th.toys[id].gapx=th.toys[id].lh;
+ gbox.blit(gbox.getBufferContext(),gbox.getImage(data.image),{dh:th.toys[id].cnt,dw:th.toys[id].lw,dx:data.x,dy:data.y+th.toys[id].lh-th.toys[id].cnt,alpha:data.alpha});
+ if (data.reflex) gbox.blit(gbox.getBufferContext(),gbox.getImage(data.image),{dh:th.toys[id].cnt,dw:th.toys[id].lw,dx:data.x,dy:data.y+th.toys[id].lh,alpha:data.reflex,flipv:true});
+ if (th.toys[id].cnt>=th.toys[id].lh)
+ if (data.audioreach) gbox.hitAudio(data.audioreach);
+ return toys._toybusy(th,id);
+ } else {
+ gbox.blitAll(gbox.getBufferContext(),gbox.getImage(data.image),{dx:data.x,dy:data.y});
+ if (data.reflex) gbox.blitAll(gbox.getBufferContext(),gbox.getImage(data.image),{dx:data.x,dy:data.y+th.toys[id].lh,alpha:data.reflex,flipv:true});
+
+ return toys._toydone(th,id);
+ }
+ },
+
+ /**
+ *
+ */
+ bounce:function(th,id,data) {
+ if (toys._maketoy(th,id)) {
+ th.toys[id].accy=data.accy;
+ th.toys[id].y=data.y;
+ th.toys[id].h=gbox.getImage(data.image).height;
+ th.toys[id].done=false;
+ }
+ if (!th.toys[id].done) {
+ if (th.toys[id].y+th.toys[id].h>=data.floory) {
+ if (data.audiobounce) gbox.hitAudio(data.audiobounce);
+ th.toys[id].y=data.floory-th.toys[id].h;
+ th.toys[id].accy=-Math.ceil(th.toys[id].accy/(data.heavy?data.heavy:2));
+ th.toys[id].done=(th.toys[id].accy==0);
+ } else th.toys[id].accy--;
+ th.toys[id].y-=th.toys[id].accy;
+ }
+ gbox.blitAll(gbox.getBufferContext(),gbox.getImage(data.image),{dx:data.x,dy:th.toys[id].y});
+
+ return toys._toyfrombool(th,id,th.toys[id].done);
+ }
+ },
+
+ /**
+ *
+ */
+ dialogue: {
+
+ /**
+ *
+ */
+ render:function(th,id,data){
+ if (toys._maketoy(th,id)) {
+ th.toys[id].newscene=true;
+ th.toys[id].sceneid=-1;
+ th.toys[id].ended=false;
+ th.toys[id].timer=0;
+ th.toys[id].counter=0;
+ th.toys[id].anim=0;
+ gbox.createCanvas("dialogue-"+id);
+ }
+ if (!data.hideonend||(data.hideonend&&!th.toys[id].ended)) {
+ if (th.toys[id].newscene&&!th.toys[id].ended) {
+ th.toys[id].anim=0;
+ th.toys[id].timer=0;
+ th.toys[id].newscene=false;
+ th.toys[id].sceneid++;
+ th.toys[id].ended=(th.toys[id].sceneid==data.scenes.length);
+ if (!th.toys[id].ended) {
+ th.toys[id].letter=0;
+ th.toys[id].wait=false;
+ th.toys[id].scene=data.scenes[th.toys[id].sceneid];
+ th.toys[id].fd=gbox.getFont((th.toys[id].scene.font?th.toys[id].scene.font:data.font));
+ th.toys[id].sceneH=(th.toys[id].scene.dh?th.toys[id].scene.dh:gbox.getScreenH());
+ th.toys[id].sceneW=(th.toys[id].scene.dw?th.toys[id].scene.dw:gbox.getScreenW());
+ th.toys[id].sceneX=(th.toys[id].scene.dx?th.toys[id].scene.dx:0);
+ th.toys[id].sceneY=(th.toys[id].scene.dy?th.toys[id].scene.dy:0);
+ gbox.blitClear(gbox.getCanvasContext("dialogue-"+id));
+ if (th.toys[id].scene.slide) {
+ gbox.blitAll(gbox.getCanvasContext("dialogue-"+id),gbox.getImage(th.toys[id].scene.slide.image),{dx:th.toys[id].scene.slide.x,dy:th.toys[id].scene.slide.y});
+ }
+ if (th.toys[id].scene.scroller) {
+ gbox.createCanvas("scroller-"+id,{w:th.toys[id].sceneW,h:(th.toys[id].scene.scroller.length)*(th.toys[id].fd.tileh+th.toys[id].scene.spacing)});
+ for (var i=0;ith.toys[id].scene.talk[row].length) {
+ tmp-=th.toys[id].scene.talk[row].length;
+ row++;
+ if (row==th.toys[id].scene.talk.length) {
+ row=-1;
+ break;
+ }
+ }
+ if (row>=0) {
+ gbox.blitText(gbox.getCanvasContext("dialogue-"+id),{
+ font:data.font,
+ dx:data.who[th.toys[id].scene.who].x,
+ dy:(data.who[th.toys[id].scene.who].y)+(row*th.toys[id].fd.tileh),
+ text:th.toys[id].scene.talk[row].substr(0,tmp)
+ });
+ } else
+ th.toys[id].wait=true;
+ }
+
+ } else if (th.toys[id].scene.scroller) { // SCROLLER (i.e. credits)
+
+ if (th.toys[id].counter==th.toys[id].scene.speed) {
+ th.toys[id].letter++;
+ th.toys[id].counter=0;
+ if (th.toys[id].letter==(gbox.getCanvas("scroller-"+id).height+th.toys[id].scene.push))
+ th.toys[id].wait=true;
+ }
+
+ } else if (th.toys[id].scene.bonus) { // BONUS (classic bonus award screen)
+ for (var row=0;row<=th.toys[id].letter;row++) {
+ if (th.toys[id].scene.bonus[row].text)
+ gbox.blitText(gbox.getCanvasContext("bonus-"+id),{
+ font:data.font,
+ dx:0,
+ dy:(row*(th.toys[id].fd.tileh+th.toys[id].scene.spacing)),
+ text:th.toys[id].scene.bonus[row].text
+ });
+ else if (th.toys[id].scene.bonus[row].mul) {
+ // Mask is %VAL%e%MUL%=%TOT%
+ th.toys[id].scene.bonus[row].tmptext=th.toys[id].scene.bonus[row].mask.replace(/%VAL%/,th.toys[id].timer).replace(/%MUL%/,th.toys[id].scene.bonus[row].mul).replace(/%TOT%/,(th.toys[id].timer*th.toys[id].scene.bonus[row].mul));
+ gbox.blitText(gbox.getCanvasContext("bonus-"+id),{
+ clear:true,
+ font:data.font,
+ dx:0,
+ dy:(row*(th.toys[id].fd.tileh+th.toys[id].scene.spacing)),
+ text:th.toys[id].scene.bonus[row].tmptext
+ });
+ }
+ }
+
+ if (!th.toys[id].wait) {
+ var next=false;
+ if (th.toys[id].scene.bonus[th.toys[id].letter].mul&&!th.toys[id].scene.bonus[th.toys[id].letter].text) {
+ if (th.toys[id].counter>=th.toys[id].scene.bonus[th.toys[id].letter].speed) {
+ th.toys[id].counter=0;
+ th.toys[id].timer++;
+ if (th.toys[id].timer>th.toys[id].scene.bonus[th.toys[id].letter].mulvalue) {
+ th.toys[id].scene.bonus[th.toys[id].letter].text=th.toys[id].scene.bonus[th.toys[id].letter].tmptext;
+ next=true;
+ } else {
+ if (th.toys[id].scene.bonus[th.toys[id].letter].callback)
+ th.toys[id].scene.bonus[th.toys[id].letter].callback(th.toys[id].scene.bonus[th.toys[id].letter],th.toys[id].scene.bonus[th.toys[id].letter].arg);
+ }
+ }
+
+ } else if (th.toys[id].counter>=th.toys[id].scene.speed) next=true;
+ if (next) {
+ if (th.toys[id].letter==th.toys[id].scene.bonus.length-1)
+ th.toys[id].wait=true;
+ else {
+ th.toys[id].letter++;
+ if (th.toys[id].scene.bonus[th.toys[id].letter].mul) {
+ th.toys[id].scene.bonus[th.toys[id].letter].text=null;
+ th.toys[id].scene.bonus[th.toys[id].letter].tmptext=null;
+ th.toys[id].timer=0;
+ }
+ th.toys[id].counter=0;
+ }
+ }
+ }
+ }
+
+ }
+
+ }
+
+ // RENDERING
+
+
+ if (th.toys[id].scene.talk) { // DIALOGUES
+ if (data.who[th.toys[id].scene.who].box)
+ gbox.blitRect(gbox.getBufferContext(),data.who[th.toys[id].scene.who].box);
+ if (data.who[th.toys[id].scene.who].tileset) {
+ th.toys[id].anim=(th.toys[id].anim+1)%20;
+ gbox.blitTile(gbox.getBufferContext(),{tileset:data.who[th.toys[id].scene.who].tileset,tile:help.decideFrame(th.toys[id].anim,data.who[th.toys[id].scene.who].frames),dx:data.who[th.toys[id].scene.who].portraitx,dy:data.who[th.toys[id].scene.who].portraity,camera:false,fliph:data.who[th.toys[id].scene.who].fliph,flipv:data.who[th.toys[id].scene.who].flipv});
+ }
+ gbox.blitAll(gbox.getBufferContext(),gbox.getCanvas("dialogue-"+id),{dx:0,dy:0});
+ } else if (th.toys[id].scene.scroller) // SCROLLER (i.e. credits)
+ gbox.blit(gbox.getBufferContext(),gbox.getCanvas("scroller-"+id),{dx:th.toys[id].sceneX,dy:th.toys[id].sceneY+(th.toys[id].letter=0) {
+ this.x+=this.accx;
+ this.y+=this.accy;
+ if (this.gravity) this.accy++;
+ if ((this.timer==this.toptimer)||(this.trashoffscreen&&(!gbox.objectIsVisible(this)))) gbox.trashObject(this);
+ }
+ }
+
+ obj[(data.bliton==null?"blit":data.bliton)]=function() {
+ if ((this.timer>=0)&&(!this.blinkspeed||(Math.floor(this.timer/this.blinkspeed)%2)))
+ gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.timer,this.frames),dx:this.x,dy:this.y,camera:this.camera,fliph:this.fliph,flipv:this.flipv,alpha:this.alpha});
+ }
+
+ return obj;
+ },
+
+ /**
+ *
+ */
+ popupText:function(th,group,id,data) {
+ data.text+="";
+ var fd=gbox.getFont(data.font);
+
+ var obj=gbox.addObject(
+ help.mergeWithModel(
+ data,{
+ id:id,
+ group:group,
+ x:Math.floor(th.x+th.hw-(fd.tilehw*data.text.length)),
+ y:th.y-fd.tilehh,
+ vaccy:-data.jump,
+ font:"small",
+ keep:0,
+ text:data.text+"",
+ cnt:0,
+ camera:th.camera
+ }
+ )
+ );
+
+ obj.initialize=function() {
+ var fd=gbox.getFont(this.font);
+ gbox.createCanvas("poptext-"+this.id,{w:this.text.length*fd.tilew,h:fd.tileh});
+ gbox.blitText(gbox.getCanvasContext("poptext-"+this.id),{font:this.font,text:this.text,dx:0,dy:0});
+ }
+
+ obj.onpurge=function() {
+ gbox.deleteCanvas("poptext-"+this.id);
+ }
+
+ obj[(data.logicon==null?"first":data.logicon)]=function() {
+ if (gbox.objectIsVisible(this)) {
+ if (this.vaccy)
+ this.vaccy++;
+ else
+ this.cnt++;
+ this.y+=this.vaccy;
+ if (this.cnt>=this.keep) gbox.trashObject(this);
+ } else gbox.trashObject(this);
+ }
+
+ obj[(data.bliton==null?"blit":data.bliton)]=function() {
+ gbox.blitAll(gbox.getBufferContext(),gbox.getCanvas("poptext-"+this.id),{dx:this.x,dy:this.y,camera:this.camera});
+ }
+
+ return obj;
+ },
+
+ /**
+ *
+ */
+ bounceDie:function(th,group,id,data){
+ var obj=gbox.addObject(
+ help.mergeWithModel(
+ data,{
+ id:id,
+ group:group,
+ tileset:th.tileset,
+ frame:th.frame,
+ side:th.side,
+ frames:th.frames.die,
+ x:th.x,
+ y:th.y,
+ vaccy:-data.jump,
+ accx:0,
+ flipv:data.flipv,
+ cnt:0,
+ blinkspeed:0,
+ camera:th.camera
+ }
+ )
+ );
+
+ obj[(data.logicon==null?"first":data.logicon)]=function() {
+ if (gbox.objectIsVisible(this)) {
+ this.vaccy++;
+ this.y+=this.vaccy;
+ this.x+=this.accx;
+ this.cnt++;
+ } else gbox.trashObject(this);
+ }
+
+ obj[(data.bliton==null?"blit":data.bliton)]=function() {
+ if (!this.blinkspeed||(Math.floor(this.cnt/this.blinkspeed)%2))
+ gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.cnt,this.frames),dx:this.x,dy:this.y,camera:this.camera,fliph:this.side,flipv:this.flipv});
+ }
+
+ return obj;
+ }
+ },
+
+ /**
+ *
+ */
+ audio:{
+
+ /**
+ *
+ */
+ fadeOut:function(th,group,id,data){
+ var obj=gbox.addObject(
+ help.mergeWithModel(
+ data,{
+ id:id,
+ group:group,
+ fadespeed:-0.02*(data.fadein?-1:1),
+ stoponmute:true,
+ audio:null,
+ channel:null,
+ destination:null
+ }
+ )
+ );
+
+ obj[(data.logicon==null?"first":data.logicon)]=function() {
+ if (this.destination==null)
+ if (this.audio)
+ if (this.fadespeed>0) this.destination=1; else this.destination=0;
+ else
+ if (this.fadespeed>0) this.destination=gbox.getChannelDefaultVolume(this.channel); else this.destination=0;
+ if (this.fadespeed>0) gbox.playAudio(this.audio);
+ }
+
+ obj[(data.bliton==null?"blit":data.bliton)]=function() {
+ if (this.audio) gbox.changeAudioVolume(this.audio,this.fadespeed);
+ if (this.channel) gbox.changeChannelVolume(this.channel,this.fadespeed);
+ if (
+ (this.audio&&(
+ ((this.fadespeed<0)&&(gbox.getAudioVolume(this.audio)<=this.destination))||
+ ((this.fadespeed>0)&&(gbox.getAudioVolume(this.audio)>=this.destination))
+ ))||
+ (this.channel&&(
+ ((this.fadespeed<0)&&(gbox.getChannelVolume(this.channel)<=this.destination))||
+ ((this.fadespeed>0)&&(gbox.getChannelVolume(this.channel)>=this.destination))
+ ))
+ ) {
+ if (this.channel&&this.stoponmute&&(this.fadespeed<0)) gbox.stopChannel(this.channel);
+ if (this.audio&&this.stoponmute&&(this.fadespeed<0)) gbox.stopAudio(this.audio);
+ gbox.trashObject(this);
+ }
+ }
+ }
+
+ }
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/deps/akihabara-core-1.3.1/akihabara/trigo.js b/deps/akihabara-core-1.3.1/akihabara/trigo.js
new file mode 100644
index 0000000..de75b23
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/akihabara/trigo.js
@@ -0,0 +1,76 @@
+// ---
+// Copyright (c) 2010 Francesco Cottone, http://www.kesiev.com/
+// ---
+
+/**
+ * @namespace Trigo module provides some math stuff for moving objects in a
+ * direction or following a round path.
+ */
+var trigo={
+ ANGLE_RIGHT:0,
+ ANGLE_DOWN:Math.PI*0.5,
+ ANGLE_LEFT:Math.PI,
+ ANGLE_UP:Math.PI*1.5555555,
+
+ /**
+ * Adds two angles together (radians).
+ * @param {Float} a Base angle.
+ * @param {Float} add The angle you're adding to the base angle.
+ * @returns The resultant angle, always between 0 and 2*pi.
+ */
+ addAngle:function(a,add) {
+ a=(a+add)%(Math.PI*2);
+ if (a<0) return (Math.PI*2)+a; else return a;
+ },
+ /**
+ * Gets the distance between two points.
+ * @param {Object} p1 This is an object containing x and y params for the first point.
+ * @param {Object} p2 This is an object containing x and y params for the second point.
+ * @returns The distance between p1 and p2.
+ */
+ getDistance:function(p1,p2) {
+ return Math.sqrt(Math.pow(p2.x-p1.x,2)+Math.pow(p2.y-p1.y,2))
+ },
+
+ /**
+ * Calculates the angle between two points.
+ * @param {Object} p1 This is an object containing x and y params for the first point.
+ * @param {Object} p2 This is an object containing x and y params for the second point.
+ * @param {Float} transl (Optional) Adds an angle (in radians) to the result. Defaults to 0.
+ * @returns The angle between points p1 and p2, plus transl.
+ */
+ getAngle:function(p1,p2,transl) {
+ return this.addAngle(Math.atan2(p2.y-p1.y,p2.x-p1.x),(transl?transl:0));
+ },
+
+ /**
+ * Translates a point by a vector defined by angle and distance. This does not return a value but rather modifies the x and y values of p1.
+ * @param {Object} p1 This is an object containing x and y params for the point.
+ * @param {Float} a The angle of translation (rad).
+ * @param {Float} d The distance of translation.
+ */
+ translate:function(p1,a,d) {
+ p1.x=p1.x+Math.cos(a)*d;
+ p1.y=p1.y+Math.sin(a)*d;
+ },
+
+ /**
+ * Translates an x component of a coordinate by a vector defined by angle and distance. This returns its component translation.
+ * @param {Float} x1 This is an x coordinate.
+ * @param {Float} a The angle of translation (rad).
+ * @param {Float} d The distance of translation.
+ */
+ translateX:function(x1,a,d) {
+ return x1+Math.cos(a)*d
+ },
+
+ /**
+ * Translates a y component of a coordinate by a vector defined by angle and distance. This returns its component translation.
+ * @param {Float} y1 This is a y coordinate.
+ * @param {Float} a The angle of translation (rad).
+ * @param {Float} d The distance of translation.
+ */
+ translateY:function(y1,a,d) {
+ return y1+Math.sin(a)*d;
+ }
+}
diff --git a/deps/akihabara-core-1.3.1/extra-halloffame.html b/deps/akihabara-core-1.3.1/extra-halloffame.html
new file mode 100644
index 0000000..8d455fd
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/extra-halloffame.html
@@ -0,0 +1,281 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deps/akihabara-core-1.3.1/extras/AkibaHero/akibahero-mapper.html b/deps/akihabara-core-1.3.1/extras/AkibaHero/akibahero-mapper.html
new file mode 100644
index 0000000..d35d389
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/extras/AkibaHero/akibahero-mapper.html
@@ -0,0 +1,937 @@
+
+
+ Marker tool (use with Safari/Opera - press play here and then any key)
+
+ Mapper tool
+
+
+
+
\ No newline at end of file
diff --git a/deps/akihabara-core-1.3.1/game-capman.html b/deps/akihabara-core-1.3.1/game-capman.html
new file mode 100644
index 0000000..f47528e
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/game-capman.html
@@ -0,0 +1,720 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deps/akihabara-core-1.3.1/resources/audio/capman-ingame.mp3 b/deps/akihabara-core-1.3.1/resources/audio/capman-ingame.mp3
new file mode 100644
index 0000000..0b1d858
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/capman-ingame.mp3 differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/capman-ingame.ogg b/deps/akihabara-core-1.3.1/resources/audio/capman-ingame.ogg
new file mode 100644
index 0000000..2c35939
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/capman-ingame.ogg differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/coin.mp3 b/deps/akihabara-core-1.3.1/resources/audio/coin.mp3
new file mode 100644
index 0000000..eda4dfd
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/coin.mp3 differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/coin.ogg b/deps/akihabara-core-1.3.1/resources/audio/coin.ogg
new file mode 100644
index 0000000..5cfb9c0
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/coin.ogg differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/die.mp3 b/deps/akihabara-core-1.3.1/resources/audio/die.mp3
new file mode 100644
index 0000000..9d8d0e5
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/die.mp3 differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/die.ogg b/deps/akihabara-core-1.3.1/resources/audio/die.ogg
new file mode 100644
index 0000000..da474bd
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/die.ogg differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/eat.mp3 b/deps/akihabara-core-1.3.1/resources/audio/eat.mp3
new file mode 100644
index 0000000..da3703d
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/eat.mp3 differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/eat.ogg b/deps/akihabara-core-1.3.1/resources/audio/eat.ogg
new file mode 100644
index 0000000..62c25bf
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/eat.ogg differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/halloffame-theme.mp3 b/deps/akihabara-core-1.3.1/resources/audio/halloffame-theme.mp3
new file mode 100644
index 0000000..c35c26b
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/halloffame-theme.mp3 differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/halloffame-theme.ogg b/deps/akihabara-core-1.3.1/resources/audio/halloffame-theme.ogg
new file mode 100644
index 0000000..70acf8d
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/halloffame-theme.ogg differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/laser.mp3 b/deps/akihabara-core-1.3.1/resources/audio/laser.mp3
new file mode 100644
index 0000000..6bd69a4
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/laser.mp3 differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/laser.ogg b/deps/akihabara-core-1.3.1/resources/audio/laser.ogg
new file mode 100644
index 0000000..2a54b13
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/laser.ogg differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/powerup3.mp3 b/deps/akihabara-core-1.3.1/resources/audio/powerup3.mp3
new file mode 100644
index 0000000..18b8d23
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/powerup3.mp3 differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/powerup3.ogg b/deps/akihabara-core-1.3.1/resources/audio/powerup3.ogg
new file mode 100644
index 0000000..9d2a471
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/powerup3.ogg differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/select.mp3 b/deps/akihabara-core-1.3.1/resources/audio/select.mp3
new file mode 100644
index 0000000..fd5ceec
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/select.mp3 differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/select.ogg b/deps/akihabara-core-1.3.1/resources/audio/select.ogg
new file mode 100644
index 0000000..0829a93
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/select.ogg differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/start.mp3 b/deps/akihabara-core-1.3.1/resources/audio/start.mp3
new file mode 100644
index 0000000..01de403
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/start.mp3 differ
diff --git a/deps/akihabara-core-1.3.1/resources/audio/start.ogg b/deps/akihabara-core-1.3.1/resources/audio/start.ogg
new file mode 100644
index 0000000..a6bccd4
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/audio/start.ogg differ
diff --git a/deps/akihabara-core-1.3.1/resources/capman/cels.png b/deps/akihabara-core-1.3.1/resources/capman/cels.png
new file mode 100644
index 0000000..cd76f6d
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/capman/cels.png differ
diff --git a/deps/akihabara-core-1.3.1/resources/capman/font.png b/deps/akihabara-core-1.3.1/resources/capman/font.png
new file mode 100644
index 0000000..89d9c09
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/capman/font.png differ
diff --git a/deps/akihabara-core-1.3.1/resources/capman/logo.png b/deps/akihabara-core-1.3.1/resources/capman/logo.png
new file mode 100644
index 0000000..17885d9
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/capman/logo.png differ
diff --git a/deps/akihabara-core-1.3.1/resources/halloffame/font.png b/deps/akihabara-core-1.3.1/resources/halloffame/font.png
new file mode 100644
index 0000000..301219a
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/halloffame/font.png differ
diff --git a/deps/akihabara-core-1.3.1/resources/halloffame/kesiev.png b/deps/akihabara-core-1.3.1/resources/halloffame/kesiev.png
new file mode 100644
index 0000000..bbcc12d
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/halloffame/kesiev.png differ
diff --git a/deps/akihabara-core-1.3.1/resources/halloffame/logo.png b/deps/akihabara-core-1.3.1/resources/halloffame/logo.png
new file mode 100644
index 0000000..d80063f
Binary files /dev/null and b/deps/akihabara-core-1.3.1/resources/halloffame/logo.png differ
diff --git a/deps/akihabara-core-1.3.1/tool-settings.html b/deps/akihabara-core-1.3.1/tool-settings.html
new file mode 100644
index 0000000..24a924e
--- /dev/null
+++ b/deps/akihabara-core-1.3.1/tool-settings.html
@@ -0,0 +1,204 @@
+
+
+
+
+
+ Akihabara setup
+
+
+
Akihabara setup
+
+
Gamebox buttons setup
+
These settings will be used by any Gamebox game hosted by this domain. Click a box and press a key to customize.
+
+
Up
+
Down
+
Left
+
Right
+
A Button
+
B Button
+
C Button
+
+
+
+
+
Gamebox audio setup
+
These settings will be used by any Gamebox game hosted by this domain. Audio is still in beta and is available on some browsers by default.
+
+
Disable audio
+
+
+
+
Gamebox screen setup
+
These settings will be used by any Gamebox game hosted by this domain.
+
+
Loading screen
+
Full screen effects
+
+
+
+
Gamebox experimental
+
This checkbox enables misc experimental features that are really unstable. Use at yout own risk!
+
+
Enable experimental features
+
+
Experimental features are: iPad with 3.6 firmware, iPhone with 4.x betas (single channel mode), IE9 Beta in IE7 Compat mode, latest builds of Konqueror (single channle mode) and all other audio-enabled devices (untested)
+
+
+
+
+
+
+
+
diff --git a/index.ejs b/index.ejs
new file mode 100644
index 0000000..27465b0
--- /dev/null
+++ b/index.ejs
@@ -0,0 +1,38 @@
+
+
+
+
+ WebSocket Whiteboard Example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/package.json b/package.json
index c13df3a..ae32cd7 100644
--- a/package.json
+++ b/package.json
@@ -11,8 +11,10 @@
"node": "~0.6.10"
},
"dependencies": {
- "ws" : "0.4.7",
- "mongodb" : "0.9.9-4"
+ "websocket" : "1.0.4",
+ "mongodb" : "0.9.9-4",
+ "express" : "2.5.8",
+ "ejs" : "0.6.1"
},
"devDependencies": {},
"optionalDependencies": {}
diff --git a/public/akihabara/buttons.png b/public/akihabara/buttons.png
new file mode 100644
index 0000000..3b056bb
Binary files /dev/null and b/public/akihabara/buttons.png differ
diff --git a/public/akihabara/debugfont.png b/public/akihabara/debugfont.png
new file mode 100644
index 0000000..3564b40
Binary files /dev/null and b/public/akihabara/debugfont.png differ
diff --git a/public/akihabara/dpad.png b/public/akihabara/dpad.png
new file mode 100644
index 0000000..ff536b5
Binary files /dev/null and b/public/akihabara/dpad.png differ
diff --git a/public/akihabara/fretboard.png b/public/akihabara/fretboard.png
new file mode 100644
index 0000000..52cd354
Binary files /dev/null and b/public/akihabara/fretboard.png differ
diff --git a/public/akihabara/gamecycle.js b/public/akihabara/gamecycle.js
new file mode 100644
index 0000000..bc678a8
--- /dev/null
+++ b/public/akihabara/gamecycle.js
@@ -0,0 +1,429 @@
+// ---
+// Copyright (c) 2010 Francesco Cottone, http://www.kesiev.com/
+// ---
+
+/**
+ * @namespace
+ * Gamecycle contains your basic game loop: intro, menus,
+ * crossfading between stages/lifes, gameover and ending.
+ */
+var gamecycle={
+
+ /**
+ * Gamecycle constructor - initializes a new game object
+ *
+ * @param id unique id of object
+ * @param group name of group to store the object in
+ */
+ createMaingame:function(id,group) {
+ return gbox.addObject({
+ id:id,
+ group:group,
+ counter:0,
+ difficulty:0,
+
+ // state transition
+ state:50,
+ stateFirstIteration:true,
+
+ hud:{},
+
+ /**
+ * This method is called whenever you load a new map. It's meant to be
+ * overridden when you create your game.
+ */
+ changeLevel:function() { },
+
+ /**
+ * This method is called every time a player is "reborn". This method is
+ * meant to be overridden since you have to do garbage collection.
+ */
+ newLife:function() { },
+
+ // game disclaimer animation (if needed)
+ gameDisclaimerAnimation:function(reset) {
+ return true;
+ },
+
+ // game intro animation
+ gameIntroAnimation:function(reset) {
+ if (reset) {
+ gbox.stopChannel("bgmusic");
+ toys.resetToy(this,"default-blinker");
+ } else {
+ gbox.blitFade(gbox.getBufferContext(),{alpha:1});
+ return toys.text.blink(this,"default-blinker",gbox.getBufferContext(),{font:"small",text:"LETS BEGIN!",valign:gbox.ALIGN_MIDDLE,halign:gbox.ALIGN_CENTER,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH(),blinkspeed:5,times:6});
+ }
+ },
+
+ // level intro animation
+ levelIntroAnimation:function(reset) {
+ if (reset) {
+ gbox.stopChannel("bgmusic");
+ toys.resetToy(this,"default-blinker");
+ } else {
+ gbox.blitFade(gbox.getBufferContext(),{alpha:1});
+ return toys.text.blink(this,"default-blinker",gbox.getBufferContext(),{font:"small",text:"GET READY!",valign:gbox.ALIGN_MIDDLE,halign:gbox.ALIGN_CENTER,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH(),blinkspeed:5,times:6});
+ }
+ },
+
+ // Life intro animation
+ newlifeIntroAnimation:function(reset) {
+ if (reset) {
+ gbox.stopChannel("bgmusic");
+ toys.resetToy(this,"default-blinker");
+ } else {
+ gbox.blitFade(gbox.getBufferContext(),{alpha:1});
+ return toys.text.fixed(this,"default-blinker",gbox.getBufferContext(),{font:"small",text:"GET READY!",valign:gbox.ALIGN_MIDDLE,halign:gbox.ALIGN_CENTER,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH(),time:30});
+ }
+ },
+
+ // gameover animation
+ gameoverIntroAnimation:function(reset) {
+ if (reset) {
+ gbox.stopChannel("bgmusic");
+ toys.resetToy(this,"default-blinker");
+ } else {
+ gbox.blitFade(gbox.getBufferContext(),{alpha:1});
+ return toys.text.fixed(this,"default-blinker",gbox.getBufferContext(),{font:"small",text:"GAME OVER",valign:gbox.ALIGN_MIDDLE,halign:gbox.ALIGN_CENTER,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH(),time:50});
+ }
+ },
+
+ // game title animation
+ gameTitleIntroAnimation:function(reset) {
+ if (reset)
+ gbox.stopChannel("bgmusic");
+ else {
+ gbox.blitFade(gbox.getBufferContext(),{alpha:1});
+ gbox.blitText(gbox.getBufferContext(),{font:"small",text:"GAME TITLE",valign:gbox.ALIGN_MIDDLE,halign:gbox.ALIGN_CENTER,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH()-100});
+ }
+ },
+
+ // End level animation
+ endlevelIntroAnimation:function(reset) {
+ if (reset) {
+ toys.resetToy(this,"default-blinker");
+ } else {
+ return toys.text.blink(this,"default-blinker",gbox.getBufferContext(),{font:"small",text:"WELL DONE!",valign:gbox.ALIGN_MIDDLE,halign:gbox.ALIGN_CENTER,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH(),blinkspeed:5,times:10});
+ }
+ },
+
+ // Game ending
+ gameEndingIntroAnimation:function(reset) {
+ if (reset) {
+ toys.resetToy(this,"default-blinker");
+ } else {
+ gbox.blitFade(gbox.getBufferContext(),{alpha:1});
+ return toys.text.blink(this,"default-blinker",gbox.getBufferContext(),{font:"small",text:"CONGRATULATIONS!",valign:gbox.ALIGN_MIDDLE,halign:gbox.ALIGN_CENTER,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH(),blinkspeed:5,times:10});
+ }
+ },
+
+ // PRESS START
+ pressStartIntroAnimation:function(reset) {
+ if (reset) {
+ toys.resetToy(this,"default-blinker");
+ } else {
+ toys.text.blink(this,"default-blinker",gbox.getBufferContext(),{font:"small",text:"PRESS A TO START",valign:gbox.ALIGN_MIDDLE,halign:gbox.ALIGN_CENTER,dx:0,dy:Math.floor(gbox.getScreenH()/3),dw:gbox.getScreenW(),dh:Math.floor(gbox.getScreenH()/3)*2,blinkspeed:10});
+ return gbox.keyIsHit("a");
+ }
+ },
+
+ /**
+ * This method is called when the player dies.
+ */
+ gameIsOver:function() { return true; },
+
+ /**
+ * Actions done during the game (i.e. stage is clear or other ending conditions)
+ */
+ gameEvents:function() {
+
+ },
+
+ gameMenu:function(reset) {
+ if (reset) {
+ toys.resetToy(this,"difficulty");
+ } else {
+ gbox.blitFade(gbox.getBufferContext(),{alpha:0.5});
+ if (toys.ui.menu(this,"difficulty",{audiooption:"default-menu-option",audioconfirm:"default-menu-confirm",font:"small",keys:{up:"up",down:"down",ok:"a",cancel:"b"},selector:">",items:["EASY","NORMAL","HARD"],x:10,y:10})) {
+ if (toys.getToyValue(this,"difficulty","ok") == -1) return -1;
+ else {
+ this.difficulty=toys.getToyValue(this,"difficulty","selected");
+ return true;
+ }
+ }
+ return false;
+ }
+ },
+
+ // CHECK
+
+ gameIsHold:function() { // Use this clause to check collision and kill player: if true the level is changing
+ return (this.state==400)||(this.state==401);
+ },
+
+ isCompleted:function() {
+ return (this.state==800);
+ },
+
+ // GAME CYCLE
+
+ getNextLevel:function() {
+ return this._nextlevel;
+ },
+
+ gotoLevel:function(level) {
+ this._nextlevel=level;
+ this.setState(400);
+ },
+
+ playerDied:function(data) {
+ this._loselife=data;
+ this.setState(500);
+ },
+
+ gameIsCompleted:function() {
+ this.setState(800);
+ },
+
+ // private methods
+
+ /**
+ * Changes the current game state
+ *
+ * @param st state number
+ */
+ setState:function(st) {
+ this.state=st;
+ this.stateFirstIteration=true;
+ },
+
+ /*
+ * Removes all objects in each group except the game
+ * cycle group. Used for garbage collection when resetting the game.
+ */
+ _resetGroups:function() {
+ var g=gbox.getGroups();
+ for (var i=0;iprio) break; else i=this.data[i].__next;
+ if (i==null) { // if last, chain in queue
+ obj.__next=null;
+ obj.__first=this.last;
+ this.data[this.last].__next=nid;
+ this.last=nid;
+ } else { // else reconnect objects
+ obj.__first=this.data[i].__first;
+ obj.__next=i;
+ this.data[i].__first=nid;
+ if (obj.__first!=null) this.data[obj.__first].__next=nid; else this.first=nid;
+ }
+
+ }
+ obj.__prio=prio;
+ obj.__id=nid;
+ this.data[nid]=obj;
+ return nid;
+ },
+ setPrio:function(obd,prio) {
+ if (this.data[obd].__prio==prio) return;
+ if (this.first!=this.last)
+ if (this.data[obd].__prio=prio) break; else i=this.data[i].__next;
+ if ((i==null)||(this.data[i].__first!=this.data[obd].__id)) {
+ // disconnect
+ this.disconnect(obd);
+ // Reconnect
+ if (i==null) {
+ this.data[this.last].__next=this.data[obd].__id;
+ this.data[obd].__first=this.last;
+ this.data[obd].__next=null;
+ this.last=this.data[obd].__id;
+ } else {
+ this.data[obd].__first=this.data[i].__first;
+ this.data[obd].__next=i;
+ this.data[i].__first=this.data[obd].__id;
+ if (this.data[obd].__first!=null) this.data[this.data[obd].__first].__next=this.data[obd].__id; else this.first=this.data[obd].__id;
+ }
+ }
+ }
+ } else {
+ if (this.data[obd].__id!=this.first) {
+ var i=this.data[obd].__first;
+ while (i!=null)
+ if (this.data[i].__prio<=prio) break; else i=this.data[i].__first;
+ if ((i==null)||(this.data[i].__next!=this.data[obd].__id)) {
+ // disconnect
+ this.disconnect(obd);
+ if (i==null) {
+ this.data[this.first].__first=this.data[obd].__id;
+ this.data[obd].__first=null;
+ this.data[obd].__next=this.first;
+ this.first=this.data[obd].__id;
+ } else {
+ this.data[obd].__first=i;
+ this.data[obd].__next=this.data[i].__next;
+ this.data[i].__next=this.data[obd].__id;
+ if (this.data[obd].__next!=null) this.data[this.data[obd].__next].__first=this.data[obd].__id; else this.last=this.data[obd].__id;
+ }
+ }
+ }
+ }
+ this.data[obd].__prio=prio;
+ },
+ remove:function(obd) {
+ this.disconnect(obd);
+ this.gar.push(this.data[obd].__id);
+ delete this.data[this.data[obd].__id];
+ }
+ }
+ }
+}
+
+// A special circular queue with some features useful for the resource loader
+var cyclelist={
+ create:function(size) {
+ return {
+ _head:0,
+ _tail:0,
+ _data:[],
+ _size:(size?size:10),
+ _total:0,
+ _done:0,
+ _current:null,
+ getTotal:function(){return this._total}, // Number of elements to be "poped"
+ getDone:function(){return this._done}, // Number of popped elements since the last empty
+ getSize:function(){return this._size}, // The maximum number of elements in the queue
+ isProcessing:function(){return this._current!=null}, // The last pop was not a null (i.e. the queue returned a valid object)
+ isEnded:function(){return (this._head==this._tail)}, // There are other elements in the queue
+ isBusy:function(){return this.isProcessing()||!this.isEnded()}, // There are elements in the queue/the last one pop returned an object that is being processed
+ getCurrent:function(){return this._current}, // Return the last popped element
+ push:function(d) {
+ this._data[this._head]=d;
+ this._head=(this._head+1)%this._size;
+ this._total++;
+ },
+ pop:function() {
+ if (this.isEnded()) {
+ this._total=0;
+ this._done=0;
+ this._current=null;
+ } else {
+ this._current=this._data[this._tail];
+ this._tail=(this._tail+1)%this._size;
+ this._done++;
+ }
+ return this._current;
+ },
+ dump:function() {
+ var r="";
+ for (var i=0;iimg.width) { dw=(dw/sw)*(img.width-sx);sw=img.width-sx;}
+ if (sy+sh>img.height) { dh=(dh/sh)*(img.height-sy);sh=img.height-sy;}
+ try { if ((sh>0)&&(sw>0)&&(sxgbox._autoskip.hiidle)&&(gbox._frameskip>gbox._autoskip.min)) gbox.setFrameskip(gbox._frameskip-1);
+ if (gbox._statbar) gbox.debugGetstats();
+ this._gametimer=setTimeout(gbox.go,(gbox._framestart<=0?1:gbox._framestart));
+ },
+ /**
+ * Apply FSEs to the screen. Is called each frame.
+ */
+ _applyfse:function(){
+ switch (gbox._flags.fse) {
+ case "scanlines": {
+ gbox.getBufferContext().drawImage(gbox.getCanvas("-gbox-fse"),0,0);
+ break;
+ }
+ case "lcd":{
+ if (gbox._localflags.fselcdget&&gbox.getBuffer())
+ gbox.getCanvasContext("-gbox-fse-new").drawImage(gbox.getBuffer(),0,0);
+ gbox.getBufferContext().save();
+ gbox.getBufferContext().globalAlpha=0.5;
+ gbox.getBufferContext().drawImage(gbox.getCanvas("-gbox-fse-old"),0,0);
+ gbox.getBufferContext().restore();
+ if (gbox._localflags.fselcdget)
+ gbox.swapCanvas("-gbox-fse-new","-gbox-fse-old");
+ gbox._localflags.fselcdget=!gbox._localflags.fselcdget;
+ break;
+ }
+ }
+ },
+ /**
+ * Register the code that have to be executed once the page is loaded. Usually contains game initialization, resources loading etc.
+ */
+ onLoad:function(code) {
+ this.addEventListener(window,'load',code);
+ },
+ /**
+ * This function is called once per frame. This is where the basic rendering and processing of groups occurs.
+ */
+ go:function() {
+ if (gbox._loaderqueue.isBusy()) {
+ if (gbox._gamewaiting==1) {
+ gbox.blitFade(gbox._screen.getContext("2d"),{alpha:0.5});
+ gbox.blitText(gbox._screen.getContext("2d"),{font:"_dbf",dx:2,dy:2,text:"LOADING..."});
+ gbox._gamewaiting=true;
+ }
+ if (gbox._gamewaiting<=1) {
+ var bw=Math.floor(((gbox.getScreenW()-4)*gbox._loaderqueue.getDone())/gbox._loaderqueue.getSize());
+ gbox._screen.getContext("2d").globalAlpha=1;
+ gbox._screen.getContext("2d").fillStyle = gbox._splash.gaugeLittleBackColor;
+ gbox._screen.getContext("2d").fillRect(0,4+gbox.getFont("_dbf").tileh,gbox.getScreenW(),1);
+ gbox._screen.getContext("2d").fillStyle = gbox._splash.gaugeLittleColor;
+ gbox._screen.getContext("2d").fillRect(0,4+gbox.getFont("_dbf").tileh,(bw>0?bw:0),1);
+ gbox._screen.getContext("2d").restore();
+ gbox.setStatBar("Loading... ("+gbox._loaderqueue.getDone()+"/"+gbox._loaderqueue.getTotal()+")");
+ }
+ if (gbox._gamewaiting) gbox._gamewaiting--;
+ setTimeout(gbox.go,1000);
+ } else {
+ gbox._gamewaiting=3;
+ gbox._framestart=new Date().getTime();
+ var gr="";
+ for (var g=0;g=gbox._frameskip) {
+ if (gbox._localflags.fse) gbox._applyfse();
+ if (gbox._db) gbox.blitImageToScreen(gbox.getBuffer());
+ gbox._fskid=0;
+ } else gbox._fskid++;
+
+ gbox.purgeGarbage();
+
+ if (gbox._zindexch.length) {
+
+ for (var i=0;i0?" ("+gbox._frameskip+"skip)":"")+" | ";
+ var cnt=0;
+ for (var g=0;g ";
+ var id=gbox._zindex.first;
+ while (id!=null) {
+ if (gbox._groupplay[gbox._zindex.data[id].g]) statline+=gbox._zindex.data[id].g+" | "+gbox._zindex.data[id].o+" ("+gbox._zindex.data[id].__prio+") ";
+ id=gbox._zindex.data[id].__next;
+ }
+ */
+ gbox.setStatBar(statline);
+ },
+
+ setZindex:function(th,z) {
+ if ((th.__zt==null)||(th.zindex!=z)) {
+ th.zindex=z;
+ this._zindexch.push({o:{g:th.group,o:th.id},z:z});
+ }
+ },
+
+ /**
+ * Returns true if a given key in this._keymap is pressed. Only returns true on the transition from unpressed to pressed.
+ * @param {String} id A key in the keymap. By default, one of: "up" "down" "left" "right" "a" "b" "c"
+ * @returns {Boolean} True if the given key is transitioning from unpressed to pressed in this frame.
+ */
+ keyIsHit:function(id) { return this._keyboard[this._keymap[id]]==1},
+
+ /**
+ * Returns true if a given key in this._keymap is being held down. Returns true as long as the key is held down.
+ * @param {String} id A key in the keymap. By default, one of: "up" "down" "left" "right" "a" "b" "c"
+ * @returns {Boolean} True if the given key is held down.
+ */
+ keyIsPressed:function(id) { return this._keyboard[this._keymap[id]]>0},
+
+ /**
+ * Returns true if a given key in this._keymap has been held down for at least one frame. Will not return true if a key
+ * is quickly tapped, only once it has been held down for a frame.
+ * @param {String} id A key in the keymap. By default, one of: "up" "down" "left" "right" "a" "b" "c"
+ * @returns {Boolean} True if the given key has been held down for at least one frame.
+ */
+ keyIsHold:function(id) { return this._keyboard[this._keymap[id]]>1},
+
+ /**
+ * Returns true if a given key in this._keymap is released. Only returns true on the transition from pressed to unpressed.
+ * @param {String} id A key in the keymap. By default, one of: "up" "down" "left" "right" "a" "b" "c"
+ * @returns {Boolean} True if the given key is transitioning from pressed to unpressed in this frame.
+ */
+ keyIsReleased:function(id) { return this._keyboard[this._keymap[id]]==-1},
+
+ _savesettings:function() {
+ var saved="";
+ for (var k in this._keymap) saved+="keymap-"+k+":"+this._keymap[k]+"~";
+ for (var f in this._flags) {
+ switch (this._flagstype[f]) {
+ case "check": { saved+="flag-"+f+":"+(this._flags[f]?1:0)+"~"; break; }
+ case "list": { saved+="flag-"+f+":"+this._flags[f]+"~"; break; }
+ }
+ }
+ this.dataSave("sys",saved);
+ },
+ _loadsettings:function() {
+ var cfg=this.dataLoad("sys");
+ if (cfg!==null) {
+ cfg=cfg.split("~");
+ var kv;
+ var mk;
+ for (var i=0;iviewdata.h) this._camera.y=viewdata.h-this._screenh;
+ if (this._camera.y<0) this._camera.y=0;
+ },
+
+ /**
+ * Sets the x value of the current camera object.
+ * @param {Integer} x The camera object's new x value.
+ * @param {Object} viewdata An object containing parameters h and w, which are a bounding box that the camera is
+ * not supposed to leave. For example, to use your map as a bounding area for the camera, pass along {w: map.w, h: map.h}.
+ */
+ setCameraX:function(x,viewdata) {
+ this._camera.x=x;
+ if (this._camera.x+this._camera.w>viewdata.w) this._camera.x=viewdata.w-this._screenw;
+ if (this._camera.x<0) this._camera.x=0;
+ },
+
+ /**
+ * Centers the camera.
+ * @param {Object} data An object containing x and y parameters -- typically the object you wish to center the camera on.
+ * @param {Object} viewdata An object containing parameters h and w, which are a bounding box that the camera is
+ * not supposed to leave. For example, to use your map as a bounding area for the camera, pass along {w: map.w, h: map.h}.
+ * @example
+ * // Center the camera on the player object
+ * gbox.centerCamera(gbox.getObject('player', 'player_id'), {w: map.w, h: map.h});
+ */
+ centerCamera:function(data,viewdata) {
+ this.setCameraX(data.x-this._screenhw,viewdata);
+ this.setCameraY(data.y-this._screenhh,viewdata);
+ },
+
+ /**
+ * Get an array containing the names of each group in the game, in order of rendering.
+ * @returns {Array} An array of group names.
+ * @example
+ * grouplist = gbox.getGroups();
+ * grouplist; // => ["background", "player", "enemy", "game"]
+ */
+ getGroups:function() { return this._groups; },
+
+ /**
+ * Defines the names of each group in the game along with their rendering order.
+ * @param {Array} g An array of strings of group names, in the order in which the groups should be rendered. So
+ * g[0] will contain the first group to render, g[1] the second group to render, etc.
+ */
+ setGroups:function(g){
+ this._groups=g;
+ this._groupplay[gbox.ZINDEX_LAYER]=true;
+ for (var i=0;iNOTE: this does not return a copy of the object you've requested! Any modifications you make
+ * to the object returned are directly modifying the object you requested.
+ * @param {String} group The id of the group that contains the object.
+ * @param {String} id The id of the instance of the object.
+ * @returns {Object} The object requested.
+ * @example
+ * // Find the player and reduce health by half.
+ * playertemp = gbox.getObject('player','player_id');
+ * player.health = player.health/2;
+ */
+ getObject:function(group,id) {return this._objects[group][id]},
+
+ /**
+ * Creates a font.
+ * @param {Object} data An object containing:
id: the id of the font
+ *
image: reference to the font image loaded (must contain font character tiles in ASCII order)
+ *
firstletter: the ASCII character that the font image's first character corresponds to
+ *
tileh: height in pixels of the character tiles
+ *
tilew: width in pixels of the character tiles
+ *
tilerow: width in pixels of each row in the font image
+ *
gapx: x-coord gap between tile columns, in pixels
+ *
gapy: y-coord gap between tile rows, in pixels
+ * @example
+ * gbox.addImage('font', 'font.png');
+ * gbox.addFont({ id: 'small', image: 'font', firstletter: ' ', tileh: 8, tilew: 8, tilerow: 255, gapx: 0, gapy: 0 });
+ */
+ addFont:function(data) {
+ data.tilehh=Math.floor(data.tileh/2);
+ data.tilehw=Math.floor(data.tilew/2);
+ this._fonts[data.id]=data;
+ this._fonts[data.id].firstascii=data.firstletter.charCodeAt(0);
+ },
+
+ /**
+ * Returns a font object containing data about the font.
+ * @param {String} id The id of the font, as set in gbox.addFont.
+ */
+ getFont:function(id) {
+ return this._fonts[id];
+ },
+
+ /**
+ * Deletes an object, keeping a record of its group and id in gbox._garbage.
+ * @param {Object} obj The object you wish to delete.
+ */
+ trashObject:function(obj) {
+ if (!this._garbage[obj.group]) this._garbage[obj.group]={};
+ this._garbage[obj.group][obj.id]=1;
+ obj.__trashing=true;
+ },
+
+ /**
+ * Clears the record held in gbox._garbage of what has been deleted. The "onpurge" method is called on the object before being deleted (for canvas deallocation etc.)
+ */
+ purgeGarbage:function() {
+ for (var group in this._garbage)
+ for (var id in this._garbage[group]) {
+ if (this._objects[group][id]["onpurge"]) this._objects[group][id].onpurge();
+ if (this._objects[group][id].__zt!=null)
+ this._zindex.remove(this._objects[group][id].__zt)
+ delete this._objects[group][id];
+ }
+ gbox._garbage={};
+ },
+
+ /**
+ * Deletes every object in a given group.
+ * @param {String} group The group id.
+ */
+ trashGroup:function(group) {
+ if (!this._garbage[group]) this._garbage[group]={};
+ for (var obj in this._objects[group])
+ this._garbage[group][obj]=1;
+ },
+
+ /**
+ * Returns whether an object is due to be trashed. Useful in cases you want to check if
+ * an object is marked as trash before it is actually deleted.
+ * @param {Object} o The object you're checking.
+ * @returns {Boolean} True if the object is marked as trash.
+ */
+ objectIsTrash:function(o) { return o.__trashing },
+
+ /**
+ * Creates a new game object. Generally speaking you pass a fully-defined object as the parameter (including a group, id, tileset, and so on).
+ * A group must be specified, or the program will crash. If no id is specified, then it will automatically provide
+ * an id of 'obj-XXXX' where 'XXXX' is an automatically incrementing integer. This is where the initialize, first, and blit
+ * functions are defined, as well.
+ * @param {Object} data The object you wish to create.
+ * @returns {Object} The object you created.
+ * @example
+ * data = {
+ * group: 'player',
+ * id: 'player_id',
+ * tileset: 'player_tiles',
+ * x: 0,
+ * y: 0,
+ * initialize: function() {
+ this.x = 10;
+ this.y = 10;
+ },
+ * };
+ * gbox.addObject(data);
+ */
+ addObject:function(data) {
+ // Extras
+ if (!data.id) {
+ data.id="obj-"+this._autoid;
+ this._autoid=(this._autoid+1)%1000;
+ }
+ if (data.tileset) {
+ if (data.h==null) data.h=this._tiles[data.tileset].tileh;
+ if (data.w==null) data.w=this._tiles[data.tileset].tilew;
+ if (data.hw==null) data.hw=this._tiles[data.tileset].tilehw;
+ if (data.hh==null) data.hh=this._tiles[data.tileset].tilehh;
+ }
+ this._objects[data.group][data.id]=data;
+ if (data.zindex!=null)
+ this.setZindex(this._objects[data.group][data.id],data.zindex);
+ return this._objects[data.group][data.id];
+ },
+
+ /**
+ * Returns whether a given group contains no objets.
+ * @param {String} gid The group you're checking.
+ * @returns {Boolean} True if the group contains no objects.
+ */
+ groupIsEmpty:function(gid) { for (var i in this._objects[gid]) return false; return true; },
+
+ /**
+ * Creates a new canvas. By default, the width and height is the current gbox._screenw and gbox._screenh,
+ * but it can also be set by passing in a data object with the appropriate parameters.
+ * @param {String} id The id of the new canvas.
+ * @param {Object} data (Optional) The height and width of the new canvas, contained in data.h and data.w parameters.
+ * @example
+ * gbox.createCanvas('newCanvas', {w: 640, h: 480});
+ */
+ createCanvas:function(id,data) {
+ this.deleteCanvas(id);
+ var w=(data&&data.w?data.w:this._screenw);
+ var h=(data&&data.h?data.h:this._screenh);
+ this._canvas[id]=document.createElement("canvas");
+ this._canvas[id].setAttribute('height',h);
+ this._canvas[id].setAttribute('width',w);
+ this._canvas[id].getContext("2d").save();
+ this._canvas[id].getContext("2d").globalAlpha=0;
+ this._canvas[id].getContext("2d").fillStyle = gbox.COLOR_BLACK;
+ this._canvas[id].getContext("2d").fillRect(0,0,w,h);
+ this._canvas[id].getContext("2d").restore();
+ },
+ /**
+ * Swap two canvas using their ID.
+ * @param {String} id The id of the first canvas.
+ * @param {String} id The id of the second canvas.
+ * @example
+ * gbox.swapCanvas('canvas1','canvas2');
+ */
+ swapCanvas:function(a,b) {
+ var swp=this._canvas[a];
+ this._canvas[a]=this._canvas[b];
+ this._canvas[b]=swp;
+ },
+ /**
+ * Deletes a given canvas.
+ * @param {String} id The id of the canvas to be deleted.
+ */
+ deleteCanvas:function(id) {
+ if (this._canvas[id]) delete this._canvas[id];
+ },
+
+ /**
+ * Checks to see if an image was successfully loaded.
+ * @param {String} id The id of the image.
+ * @returns {Boolean} True if the image has been loaded.
+ */
+ imageIsLoaded:function(id){ return this._images[id]&&(this._images[id].getAttribute("wasloaded"))&&this._images[id].width },
+
+ /**
+ * Gets information about a loaded image.
+ * @param {String} id The id of the image.
+ * @returns {Object} A DOM Image element, including the URL and last modified date of the image, its ID, and whether it was loaded successfully.
+ * @example
+ * image = gbox.getImage('logo');
+ * image; // => ?
+ */
+ getImage:function(id){return this._images[id]},
+
+ /**
+ * Gets the buffer canvas (automatically created by gbox.initScreen).
+ * @returns {Object} A DOM Canvas element, including the width and height of the canvas.
+ */
+ getBuffer:function(){return (gbox._fskid>=gbox._frameskip?(this._db?this.getCanvas("_buffer"):this._screen):null)},
+
+ /**
+ * Gets the buffer canvas context.
+ * @returns {Object} A DOM Canvas context object.
+ */
+ getBufferContext:function(){ return (gbox._fskid>=gbox._frameskip?(this._db?this.getCanvasContext("_buffer"):this._screen.getContext("2d")):null) },
+
+ /**
+ * Gets a given canvas.
+ * @param {Object} id The identifier of the canvas.
+ * @returns {Object} A DOM Canvas element, including the width and height of the canvas.
+ */
+ getCanvas:function(id){return this._canvas[id]},
+
+ /**
+ * Gets the two-dimensional canvas context of a given canvas. The object it returns contains all the drawing functions for the canvas.
+ * See W3C and
+ * Mozilla Developer Center for details.
+ * @param {Object} id The identifier of the canvas.
+ * @returns {Object} A DOM Canvas context object.
+ */
+ getCanvasContext:function(id){return this.getCanvas(id).getContext("2d");},
+
+ /**
+ * Adds an image file to the loader, assigning it to an ID. If adding an image to an existing ID, it checks to see if the file you're
+ * adding is different than the one currently assigned to the ID. If it's different, it overwrites the old image. If it's the same, then
+ * no action is taken.
+ * @param {String} id The identifier of the image.
+ * @param {String} filename The file name of the image.
+ */
+ addImage:function(id,filename) {
+ if (this._images[id])
+ if (this._images[id].getAttribute("src_org")==filename)
+ return;
+ else
+ delete this._images[id];
+ this._addtoloader({type:"image",id:id,filename:filename});
+ },
+
+ /**
+ * Deletes an image currently in use. Does not delete the image file, but removes it from Akihabara's image list.
+ * @param {String} id The identifier of the image.
+ */
+ deleteImage:function(id) {
+ delete this._images[id];
+ },
+
+ /**
+ * Creates a new Akihabara tileset, adding it to the engine.
+ * @param {Object} t An object containing:
id {String}: the new id of the tileset
+ *
image {String}: reference to the tileset image loaded
+ *
tileh {Integer}: height in pixels of the tiles
+ *
tilew {Integer}: width in pixels of the tiles
+ *
tilerow {Integer}: width in pixels of each row in the font image
+ *
gapx {Integer}: x-coord gap between tile columns, in pixels
+ *
gapy {Integer}: y-coord gap between tile rows, in pixels
+ */
+ addTiles:function(t) {
+ t.tilehh=Math.floor(t.tileh/2);
+ t.tilehw=Math.floor(t.tilew/2);
+ this._tiles[t.id]=t;
+ },
+
+ /**
+ * Gets an Akihabara tileset, adding it to the engine.
+ * @param {String} t The ID of a tileset.
+ * @returns An object containing:
id {String}: the new id of the tileset
+ *
image {String}: reference to the tileset image loaded
+ *
tileh {Integer}: height in pixels of the tiles
+ *
tilew {Integer}: width in pixels of the tiles
+ *
tilerow {Integer}: width in pixels of each row in the font image
+ *
gapx {Integer}: x-coord gap between tile columns, in pixels
+ *
gapy {Integer}: y-coord gap between tile rows, in pixels
+ */
+ getTiles:function(t) { return this._tiles[t] },
+
+ /**
+ * Loads the initial splash screen and debugging font, then calls gbox._waitforloaded which adds to the game all the previously
+ * defined resources. Once gbox._waitforloaded is done, it calls the callback function cb.
+ * @params {String} cb The name of the function to be called when all assets are done loading.
+ */
+ loadAll:function(cb) {
+ // Setup logger
+ if (this._canlog) this.log=console.log;
+ // Set the callback function, which is called after the resources are loaded.
+ if (!this._cb) this._cb = cb;
+ // Default stuff
+ this.addImage("_dbf","akihabara/debugfont.png");
+ if (this._splash.background) this.addImage("_splash",this._splash.background);
+ gbox.addFont({id:"_dbf",image:"_dbf",firstletter:" ",tileh:5,tilew:4,tilerow:16,gapx:0,gapy:0});
+ if (!gbox._splash.minimalTime)
+ gbox._minimalexpired=2;
+ this._waitforloaded();
+ },
+
+ _implicitsargs:function(data) {
+ if (data.camera) {
+ data.dx-=this._camera.x;
+ data.dy-=this._camera.y;
+ }
+ if (data.sourcecamera) {
+ data.x=this._camera.x*(data.parallaxx?data.parallaxx:1);
+ data.y=this._camera.y*(data.parallaxy?data.parallaxy:1);
+ }
+ },
+
+ /**
+ * Draws a tile to a canvas context
+ * @param {Object} tox The canvas context to be drawn on.
+ * @param {Object} data An object containing data about the tile to be drawn, including:
+ *
tileset {String}: the id of the tileset
+ *
tile {Integer}: the index of the tile within the tileset to be drawn
+ *
dx {Integer}: x coordinate to draw the tile at
+ *
dy {Integer}: y coordinate to draw the tile at
+ *
fliph {Integer}: horizontal flip, either 1 or -1
+ *
flipv {Integer}: vertical flip, either 1 or -1
+ *
alpha {Float}: alpha value (0 is transparent, 1 is opaque)
+ * @example
+ * // from capman, draws an current object's tile, called from inside its blit function
+ * gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:this.frame,dx:this.x,dy:this.y,fliph:this.fliph,flipv:this.flipv,camera:this.camera,alpha:1});
+ */
+ blitTile:function(tox,data) {
+ if (tox==null) return;
+ var ts=this._tiles[data.tileset];
+ var img=this.getImage(ts.image);
+ this._implicitsargs(data);
+ tox.save();
+ tox.globalAlpha=(data.alpha?data.alpha:1);
+ tox.translate((data.fliph?ts.tilew:0), (data.flipv?ts.tileh:0)); tox.scale((data.fliph?-1:1), (data.flipv?-1:1));
+ this._safedrawimage(tox,img, ts.gapx+(ts.tilew*(data.tile%ts.tilerow)),ts.gapy+(ts.tileh*Math.floor(data.tile/ts.tilerow)),(data.w==null?ts.tilew:data.w),(data.h==null?ts.tileh:data.h),data.dx*(data.fliph?-1:1),data.dy*(data.flipv?-1:1),(data.w?data.w:ts.tilew),(data.h?data.h:ts.tileh));
+ tox.restore();
+ },
+
+ /**
+ * Draws an image to a canvas context
+ * @param {Object} tox The canvas context to be drawn on.
+ * @param {Object} image The image to draw. Must be a DOM Image element, typicallly accessed via gbox.getImage
+ * @param {Object} data An object containing data about the tile to be drawn, including:
+ *
dx {Integer}: (required) x coordinate to draw the image at
+ *
dy {Integer}: (required) y coordinate to draw the image at
+ *
fliph {Integer}: horizontal flip, either 1 or -1
+ *
flipv {Integer}: vertical flip, either 1 or -1
+ *
alpha {Float}: alpha value (0 is transparent, 1 is opaque)
+ * @example
+ * // draw an image at (100,100)
+ * gbox.blitAll(gbox.getBufferContext(),gbox.getImage("image_id"),{dx:100,dy:100});
+ */
+ blitAll:function(tox,image,data) {
+ if (tox==null) return;
+ this._implicitsargs(data);
+ tox.save();
+ tox.globalAlpha=(data.alpha?data.alpha:1);
+ tox.translate((data.fliph?image.width:0), (data.flipv?image.height:0)); tox.scale((data.fliph?-1:1), (data.flipv?-1:1));
+ this._safedrawimage(tox,image, 0,0, image.width,image.height,data.dx*(data.fliph?-1:1),data.dy*(data.flipv?-1:1),image.width,image.height);
+ tox.restore();
+ },
+
+ blit:function(tox,image,data) {
+ if (tox==null) return;
+ this._implicitsargs(data);
+ tox.save();
+ tox.globalAlpha=(data.alpha?data.alpha:1);
+ tox.translate((data.fliph?data.dw:0), (data.flipv?data.dh:0)); tox.scale((data.fliph?-1:1), (data.flipv?-1:1));
+ this._safedrawimage(tox,image,(data.x?data.x:0), (data.y?data.y:0),(data.w?data.w:data.dw),(data.h?data.h:data.dh),data.dx*(data.fliph?-1:1),data.dy*(data.flipv?-1:1),data.dw,data.dh);
+ tox.restore();
+ },
+
+
+ /**
+ * Draws a tilemap to a canvas context
+ * @param {Object} tox The canvas context to be drawn on.
+ * @param {Object} data An object containing a set of tilemap data, including:
+ *
tileset {String}: (required) the id of the tileset the tilemap is based on
+ *
map {Array}: an array whose x and y coord represent the tilemap coordinates, containing integers that correspond to the index of a given tile (or null for no tile)
+ */
+ blitTilemap:function(tox,data) {
+ if (tox==null) return;
+ var ts=this._tiles[data.tileset];
+ for (var y=0;y
font {String}: (required) the id of font to draw the text with
+ *
text {String}: (required) the text to display
+ *
dx {Integer}: (required) the x coordinate to draw the text at
+ *
dy {Integer}: (required) the y coordinate to draw the text at
+ *
dw {Integer}: the width of the text area -- required if you define data.halign
+ *
dh {Integer}: the height of the text area -- required if you define data.valign
+ *
valign {Integer}: either gbox.ALIGN_BOTTOM (aligns from the bottom of the text area) or gbox.ALIGN_MIDDLE (vertically centers text in text area)
+ *
halign {Integer}: either gbox.ALIGN_RIGHT (aligns to the right hand side of text area) or gbox.ALIGN_CENTER (horizontallly centers text in text area)
+ *
alpha {Float}: alpha value (0 is transparent, 1 is opaque)
+ */
+ blitText:function(tox,data) {
+ if (tox==null) return;
+ data.text+=""; // Convert to string.
+ var fn=this._fonts[data.font];
+ var tile=0;
+ this._implicitsargs(data);
+ var dx=data.dx;
+ var dy=data.dy;
+ if (data.valign==gbox.ALIGN_BOTTOM) dy = dy+data.dh-fn.tileh;
+ else if (data.valign==gbox.ALIGN_MIDDLE) dy = dy+Math.floor(data.dh/2)-fn.tileh;
+ if (data.halign==gbox.ALIGN_RIGHT) dx = dx+data.dw-(data.text.length*fn.tilew);
+ else if (data.halign==gbox.ALIGN_CENTER) dx = dx+Math.floor((data.dw-(data.text.length*fn.tilew))/2);
+ tox.save();
+ tox.globalAlpha=(data.alpha?data.alpha:1);
+ for (var y=0;y=0) {
+ if (data.clear) tox.clearRect(dx+(y*fn.tilew),dy,(data.w?data.w:fn.tilew),(data.h?data.h:fn.tileh));
+ this._safedrawimage(tox,this.getImage(fn.image), fn.gapx+(fn.tilew*(tile%fn.tilerow)),
+ fn.gapy+(fn.tileh*Math.floor(tile/fn.tilerow)),fn.tilew,fn.tileh,dx+(y*fn.tilew),dy,(data.w?data.w:fn.tilew),(data.h?data.h:fn.tileh));
+ }
+ }
+ tox.restore();
+ },
+
+ /**
+ * Clears a rectangular area of a canvas context.
+ * @param {Object} image The canvas context to be drawn on.
+ * @param {Object} data An object containing a set of data, including:
+ *
x {Integer}: (required) the x coordinate of the top-left corner of the rectangle
+ *
y {Integer}: (required) the y coordinate of the top-left corner of the rectangle
+ *
w {Integer}: the width of the box; defaults to canvas width
+ *
h {Integer}: the height the box; defaults to canvas height
+ */
+ blitClear:function(image,data) {
+ if (image==null) return;
+ if (data==null) data={x:0,y:0};
+ this._implicitsargs(data);
+ image.clearRect(data.x,data.y,(data.w==null?image.canvas.width:data.w),(data.h==null?image.canvas.height:data.h));
+ },
+
+ /**
+ * Draws an image directly to the screen's current canvas context. Used internally in gbox.go(). Probably shouldn't be used otherwise.
+ */
+ blitImageToScreen:function(image) {
+ this._screen.getContext("2d").drawImage(image,0,0);
+ },
+
+ /**
+ * Draws a filled rectangle over an entire canvas context.
+ * @param {Object} tox The canvas context to be filled.
+ * @param {Object} data An object containing a set of data, including:
+ *
alpha {Float}: the alpha value of the rectangle; defaults to 1
+ *
color {Object}: the color of the box, formatted rgb(rValue, gValue, bValue); default black
+ */
+ blitFade:function(tox,data) {
+ if (tox) this.blitRect(tox,{x:0,y:0,w:tox.canvas.width,h:tox.canvas.height,alpha:data.alpha,color:data.color});
+ },
+
+ /**
+ * Draws a filled rectangle to a canvas context.
+ * @param {Object} tox The canvas context to be drawn on.
+ * @param {Object} data An object containing a set of data, including:
+ *
x {Integer}: (required) the x coordinate of the top-left corner of the rectangle
+ *
y {Integer}: (required) the y coordinate of the top-left corner of the rectangle
+ *
w {Integer}: (required) the width of the box
+ *
h {Integer}: (required) the height the box
+ *
alpha {Float}: the alpha value of the rectangle; defaults to 1
+ *
color {Object}: the color of the box, formatted rgb(rValue, gValue, bValue); default black
+ */
+ blitRect:function(tox,data) {
+ if (tox==null) return;
+ tox.save();
+ tox.globalAlpha=(data.alpha?data.alpha:1);
+ tox.fillStyle = (data.color?data.color:gbox.COLOR_BLACK);
+ tox.fillRect(data.x,data.y,data.w,data.h);
+ tox.restore();
+ },
+
+ /**
+ * Calculates a box collision between two collision boxes within a given tolerance. A higher tolerance means less precise collision.
+ * @param {Object} o1 A collision box you're testing for collision. Must contain:
+ *
x {Integer}: (required) the x coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
+ *
y {Integer}: (required) the y coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
+ *
w {Integer}: (required) the width of the box
+ *
h {Integer}: (required) the height the box
+ * @param {Object} o2 A collision box you're testing for collision. Must contain:
+ *
x {Integer}: (required) the x coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
+ *
y {Integer}: (required) the y coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
+ *
w {Integer}: (required) the width of the box
+ *
h {Integer}: (required) the height the box
+ * @param {Integer} t The tolerance for the collision, in pixels. A value of 0 means pixel-perfect box collision. A value of 2 would mean that the
+ * boxes could overlap by up to 2 pixels without being considered a collision.
+ * @returns True if the two collision boxes are colliding within the given tolerance.
+ */
+ collides:function(o1,o2,t) {
+ if (!t) t=0;
+ return !((o1.y+o1.h-1-t o2.y+o2.h-1-t) || (o1.x+o1.w-1-to2.x+o2.w-1-t));
+ },
+
+ /**
+ * Calculates a point-box collision between a point and a collision box within a given tolerance. A higher tolerance means less precise collision.
+ * @param {Object} o1 A point you're testing for collision. Must contain:
+ *
x {Integer}: (required) the x coordinate of the point
+ *
y {Integer}: (required) the y coordinate of the point
+ * @param {Object} o2 A collision box you're testing for collision. Must contain:
+ *
x {Integer}: (required) the x coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
+ *
y {Integer}: (required) the y coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
+ *
w {Integer}: (required) the width of the box
+ *
h {Integer}: (required) the height the box
+ * @param {Integer} t The tolerance for the collision, in pixels. A value of 0 means pixel-perfect collision. A value of 2 would mean that the
+ * point could exist within the outermost 2 pixels of the box without being considered a collision.
+ * @returns True if the point is colliding with the box within the given tolerance.
+ */
+ pixelcollides:function(o1,o2,t) {
+ if (!t) t=0;
+ return !((o1.y o2.y+o2.h-1-t) || (o1.xo2.x+o2.w-1-t));
+ },
+
+ /**
+ * Determines whether an object is visible by seeing if it collides with the camera's viewport.
+ * @param {Object} obj The object you're testing to see if it's visible. Must contain:
+ *
x {Integer}: (required) the x coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
+ *
y {Integer}: (required) the y coordinate of the object's origin; assumes the Akihabara default of top-left being the origin
+ *
w {Integer}: (required) the width of the object's collision box
+ *
h {Integer}: (required) the height the object's box
+ * @returns True if the object's collision box is within the camera's viewport.
+ */
+ objectIsVisible:function(obj) { return this.collides(obj,this._camera,0); },
+
+ // ---
+ // ---
+ // --- AUDIO ENGINE
+ // ---
+ // ---
+
+ _audiochannels:{},
+ _audiomastervolume:1.0,
+ _canaudio:false,
+ _audiodequeuetime:0,
+ _audioprefetch:0.5,
+ _audiocompatmode:0, // 0: pause/play, 1: google chrome compatibility, 2: ipad compatibility (single channel)
+ _createmode:0, // 0: clone, 1: rehinstance
+ _fakecheckprogressspeed:100, // Frequency of fake audio monitoring
+ _fakestoptime:1, // Fake audio stop for compatibility mode
+ _audioteam:2,
+ _loweraudioteam:1,
+ _audio:{lding:null,qtimer:false,aud:{},ast:{}},
+ _audioactions:[],
+ _showplayers:false,
+ _singlechannelname:"bgmusic",
+ _positiondelay:0,
+ _playerforcer:0,
+ _forcedmimeaudio:null,
+ _singlechannelaudio:false,
+ _audiomutevolume:0.0001, // Zero is still not accepted by everyone :(
+ _rawstopaudio:function(su) {
+ if (gbox._audiocompatmode==1) {
+ if (su.duration-su.currentTime>gbox._fakestoptime)
+ su.currentTime=su.duration-gbox._fakestoptime;
+ su.muted=true;
+ } else
+ su.pause();
+
+ },
+ _rawplayaudio:function(su) {
+ if (gbox._audiocompatmode==1) {
+ try { su.currentTime=0; } catch (e) {}
+ su.muted=false;
+ su.play();
+ } else if (gbox._audiocompatmode==2) {
+ su.load();
+ gbox._playerforcer=setInterval(function(e){try{su.play();clearInterval(gbox._playerforcer)}catch(e){}},1000);
+ } else {
+ try { su.currentTime=0; } catch (e) {}
+ su.play();
+ }
+ },
+ _finalizeaudio:function(ob,who,donext){
+
+ var cur=(who?who:this);
+ gbox.removeEventListener(cur,'ended', gbox._finalizeaudio);
+ gbox.removeEventListener(cur,'timeupdate', gbox._checkprogress);
+
+ gbox.addEventListener(cur,'ended', gbox._playbackended);
+ if (donext) gbox._loaderloaded();
+ },
+ _audiodoload:function() {
+ if (gbox._audiocompatmode==1) gbox._audio.lding.muted=true;
+ else if (gbox._audiocompatmode==2)
+ gbox._finalizeaudio(null,gbox._audio.lding,true);
+ else {
+ gbox._audio.lding.load();
+ gbox._audio.lding.play();
+ }
+ },
+ _timedfinalize:function() {
+ gbox._rawstopaudio(gbox._audio.lding);
+ gbox._finalizeaudio(null,gbox._audio.lding,true);
+ },
+ _checkprogress:function() {
+ if (gbox._audio.lding.currentTime>gbox._audioprefetch) gbox._timedfinalize();
+ },
+ _fakecheckprogress:function() {
+ if (gbox._audio.lding.currentTime>gbox._audioprefetch) gbox._timedfinalize(); else setTimeout(gbox._fakecheckprogress,gbox._fakecheckprogressspeed);
+ },
+ _audiofiletomime:function(f) {
+ var fsp=f.split(".");
+ switch (fsp.pop().toLowerCase()) {
+ case "ogg": { return "audio/ogg"; break }
+ case "mp3": { return "audio/mpeg"; break }
+ default: {
+ return "audio/mpeg";
+ }
+ }
+ },
+ _pushaudio:function(){try {this.currentTime=1.0} catch(e){} },
+ _createnextaudio:function(cau) {
+ if (cau.def) {
+ gbox.deleteAudio(cau.id);
+ this._audio.aud[cau.id]=[];
+ this._audio.ast[cau.id]={cy:-1,volume:1,channel:null,play:false,mute:false,filename:cau.filename[0]};
+ if (cau.def) for (var a in cau.def) this._audio.ast[cau.id][a]=cau.def[a];
+ }
+ if ((gbox._createmode==0)&&(cau.team>0)) {
+ var ael =this._audio.aud[cau.id][0].cloneNode(true);
+ gbox._finalizeaudio(null,ael,false);
+ } else {
+ var ael=document.createElement('audio');
+ ael.volume=gbox._audiomutevolume;
+ }
+ if (!gbox._showplayers) {
+ ael.style.display="none";
+ ael.style.visibility="hidden";
+ ael.style.width="1px";
+ ael.style.height="1px";
+ ael.style.position="absolute";
+ ael.style.left="0px";
+ ael.style.top="-1000px";
+ }
+ ael.setAttribute('controls',gbox._showplayers);
+ ael.setAttribute('aki_id',cau.id);
+ ael.setAttribute('aki_cnt',cau.team);
+ gbox.addEventListener(ael,'loadedmetadata', gbox._pushaudio); // Push locked audio in safari
+ if (((gbox._createmode==0)&&(cau.team==0))||(gbox._createmode==1)) {
+ if (gbox._forcedmimeaudio) {
+ for (var i=0;i0)
+ this._addqueue({t:1,a:ael});
+ }
+ }
+ },
+ setSplashSettings:function(a) { for (var n in a) this._splash[n]=a[n]; },
+ resetChannel:function(ch) {
+ if (this._canaudio&&this._audiochannels[ch])
+ if (ch=="master")
+ for (var ch in this._audiochannels)
+ this.setChannelVolume(ch,this._audiochannels[ch]._def.volume);
+ else if (this._audiochannels[ch])
+ this.setChannelVolume(ch,this._audiochannels[ch]._def.volume);
+ },
+ getChannelDefaultVolume:function(ch) {
+ if (this._canaudio&&this._audiochannels[ch]) return this._audiochannels[ch]._def.volume; else return null;
+ },
+ setChannelVolume:function(ch,a) {
+ if (this._canaudio&&this._audiochannels[ch]) {
+ if (ch=="master") this._audiomastervolume=a; else this._audiochannels[ch].volume=a
+ for (var j in gbox._audio.aud)
+ if (this._audio.ast[j].cy>-1) this._updateaudio(j);
+ }
+ },
+ getChannelVolume:function(ch) { if (ch=="master") return this._audiomastervolume; else if (this._audiochannels[ch]) return this._audiochannels[ch].volume; else return 0 },
+ changeChannelVolume:function(ch,a) {
+ if (this._canaudio&&this._audiochannels[ch]) {
+ var vol=this.getChannelVolume(ch)+a;
+ if (vol>1) vol=1; else if (vol<0) vol=0;
+ this.setChannelVolume(ch,vol);
+ }
+ },
+ stopChannel:function(ch) {
+ if (this._canaudio)
+ for (var j in gbox._audio.aud)
+ if (this._audio.ast[j].cy>-1&&gbox._audio.ast[j].play&&((ch=="master")||(this._audio.ast[j].channel==ch)))
+ this.stopAudio(j);
+ },
+
+ setAudioUnmute:function(a) { if (this._canaudio&&this._audio.ast[a]) { this._audio.ast[a].mute=false; this._updateaudio(a); } },
+ setAudioMute:function(a) { if (this._canaudio&&this._audio.ast[a]) { this._audio.ast[a].mute=true; this._updateaudio(a); } },
+ getAudioMute:function(a) { if (this._canaudio&&this._audio.ast[a]) return this._audio.ast[a].mute; else return null},
+
+ setAudioVolume:function(a,vol) { if (this._canaudio&&this._audio.ast[a]) { this._audio.ast[a].volume=vol; this._updateaudio(a); } },
+ getAudioVolume:function(a,vol) { if (this._canaudio&&this._audio.ast[a]) return this._audio.ast[a].volume; else return null},
+
+ setAudioPosition:function(a,p) { if (this._canaudio&&this._audio.ast[a]&&this._audio.aud[a][this._audio.ast[a].cy]) this._audio.aud[a][this._audio.ast[a].cy].currentTime=p;},
+ getAudioPosition:function(a) {if (this._canaudio&&this._audio.ast[a]&&this._audio.aud[a][this._audio.ast[a].cy]) if (this._audio.aud[a][this._audio.ast[a].cy].currentTime>this._positiondelay) return this._audio.aud[a][this._audio.ast[a].cy].currentTime-this._positiondelay; else return 0; else return 0},
+
+ getAudioDuration:function(a) {if (this._canaudio&&this._audio.ast[a]&&this._audio.aud[a][this._audio.ast[a].cy]) return this._audio.aud[a][this._audio.ast[a].cy].duration; else return 0},
+
+ changeAudioVolume:function(a,vol) { if (this._canaudio&&this._audio.ast[a]) { if (this._audio.ast[a].volume+vol>1) this._audio.ast[a].volume=1; else if (this._audio.ast[a].volume+vol<0) this._audio.ast[a].volume=0; else this._audio.ast[a].volume+=vol; this._updateaudio(a); } },
+ setCanAudio:function(a) { this._canaudio=!this._flags.noaudio&&a;},
+ setForcedMimeAudio:function(a){ this._forcedmimeaudio=a;},
+ setAudioChannels:function(a){
+ this._audiochannels=a;
+ for (var ch in a) {
+ this._audiochannels[ch]._def={};
+ for (var attr in this._audiochannels[ch])
+ if (attr!="_def") this._audiochannels[ch]._def[attr]=this._audiochannels[ch][attr];
+ }
+ },
+ setAudioTeam:function(a){ this._audioteam=a; },
+ setLowerAudioTeam:function(a){ this._loweraudioteam=a; },
+
+ // ---
+ // ---
+ // --- DYNAMIC SCRIPT INCLUSION
+ // ---
+ // ---
+
+ addScript:function(call) {
+ gbox._addtoloader({type:"script",call:call});
+ },
+
+ // ---
+ // ---
+ // --- BUNDLES
+ // ---
+ // ---
+
+ addBundle:function(call){
+ gbox._addtoloader({type:"bundle",call:call});
+ },
+
+ readBundleData:function(pack,call) {
+ // Local resources first
+ if (pack.setObject) for (var i=0;igbox.getScreenH()) l=gbox.getScreenH()-p;
+ tox.fillStyle = gbox.PALETTES.c64.colors[gbox.PALETTES.c64.order[Math.floor(Math.random()*gbox.PALETTES.c64.order.length)]];
+ tox.fillRect(0,p,gbox.getScreenW(),l);
+ p+=l;
+ }
+ tox.fillStyle = gbox.PALETTES.c64.colors.lightblue;
+ tox.fillRect(Math.floor(gbox.getScreenW()/10),Math.floor(gbox.getScreenH()/10),gbox.getScreenW()-Math.floor(gbox.getScreenW()/5),gbox.getScreenH()-Math.floor(gbox.getScreenH()/5));
+ if (gbox._splash.minilogo&&gbox.imageIsLoaded("logo")) {
+ var dw=gbox.getScreenW()/4;
+ var dh=(gbox.getImage("logo").height*dw)/gbox.getImage("logo").width;
+ gbox.blit(tox,gbox.getImage(gbox._splash.minilogo),{w:gbox.getImage("logo").width,h:gbox.getImage("logo").height,dx:(gbox.getScreenW()-dw)/2,dy:(gbox.getScreenH()-dh)/2,dw:dw,dh:dh});
+ }
+ break;
+ }
+ default:{
+ if (gbox._splash.background&&gbox.imageIsLoaded("_splash"))
+ gbox.blit(tox,gbox.getImage("_splash"),{w:gbox.getImage("_splash").width,h:gbox.getImage("_splash").height,dx:0,dy:0,dw:gbox.getScreenW(),dh:gbox.getScreenH()});
+ if (gbox._splash.minilogo&&gbox.imageIsLoaded("logo")) {
+ var dw=gbox.getScreenW()/4;
+ var dh=(gbox.getImage("logo").height*dw)/gbox.getImage("logo").width;
+ gbox.blit(tox,gbox.getImage(gbox._splash.minilogo),{w:gbox.getImage("logo").width,h:gbox.getImage("logo").height,dx:gbox.getScreenW()-dw-5,dy:gbox.getScreenH()-dh-5,dw:dw,dh:dh});
+ }
+ if (gbox._splash.footnotes&&gbox.imageIsLoaded("_dbf")) {
+ if (!gbox.getCanvas("_footnotes")) {
+ var fd=gbox.getFont("_dbf");
+ gbox.createCanvas("_footnotes",{w:gbox.getScreenW()-5,h:(gbox._splash.footnotes.length)*(fd.tileh+gbox._splash.footnotesSpacing)});
+ for (var i=0;i0?bw:0),gbox._splash.gaugeHeight-2);
+ }
+ }
+ }
+ tox.restore();
+ gbox.setStatBar("Loading... ("+gbox._loaderqueue.getDone()+"/"+gbox._loaderqueue.getTotal()+")");
+ setTimeout(gbox._waitforloaded,50);
+ } else {
+ gbox.deleteImage("_splash");
+ gbox.setStatBar();
+ gbox._cb();
+ }
+ },
+ clearCache:function() { this._loadercache.clear(); },
+
+ // ---
+ // ---
+ // --- BROWSER QUIRKS
+ // ---
+ // ---
+
+ checkCanvasSupport:function() {
+ return !!document.createElement('canvas').getContext;
+ },
+ addEventListener:function(to,event,code) {
+ if (to.addEventListener) to.addEventListener(event,code,false);
+ else to.attachEvent('on'+event,code);
+ },
+ removeEventListener:function(to,event,code) {
+ if (to.removeEventListener) to.removeEventListener(event,code,false);
+ else to.detachEvent('on'+event,code);
+ },
+ XMLHttpFactories:[
+ function () {return new XMLHttpRequest()},
+ function () {return new ActiveXObject("Msxml2.XMLHTTP")},
+ function () {return new ActiveXObject("Msxml3.XMLHTTP")},
+ function () {return new ActiveXObject("Microsoft.XMLHTTP")}
+ ],
+ createXmlHttpRequest:function() {
+ var xmlhttp=false;
+ /* running locally on IE5.5, IE6, IE7 */ ; /*@cc_on
+ if(location.protocol=="file:"){
+ if(!xmlhttp)try{ xmlhttp=new ActiveXObject("MSXML2.XMLHTTP"); }catch(e){xmlhttp=false;}
+ if(!xmlhttp)try{ xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); }catch(e){xmlhttp=false;}
+ } ; @cc_off @*/
+ /* IE7, Firefox, Safari, Opera... */
+ if(!xmlhttp)try{ xmlhttp=new XMLHttpRequest(); }catch(e){xmlhttp=false;}
+ /* IE6 */
+ if(typeof ActiveXObject != "undefined"){
+ if(!xmlhttp)try{ xmlhttp=new ActiveXObject("MSXML2.XMLHTTP"); }catch(e){xmlhttp=false;}
+ if(!xmlhttp)try{ xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); }catch(e){xmlhttp=false;}
+ }
+ /* IceBrowser */
+ if(!xmlhttp)try{ xmlhttp=createRequest(); }catch(e){xmlhttp=false;}
+ return xmlhttp;
+ }
+
+};
+
diff --git a/public/akihabara/help.js b/public/akihabara/help.js
new file mode 100644
index 0000000..2c4c5fa
--- /dev/null
+++ b/public/akihabara/help.js
@@ -0,0 +1,608 @@
+// ---
+// Copyright (c) 2010 Francesco Cottone, http://www.kesiev.com/
+// ---
+
+/**
+ * @namespace Help module provides some Javascript-specific functions, such object copying, randomizing functions,
+ * string/array handlers and the akihabaraInit function.
+ */
+var help={
+
+ /**
+ * Searches an object in an array filtering for one of their properties.
+ * @param {Array} a The array.
+ * @param {String} field The searched field.
+ * @param {String} value The searched value.
+ * @returns The found object, otherwise null.
+ */
+ searchObject:function(a,field,value) {
+ if (!a) return null; else
+ for (var i=0;i0 means you're falling onto something else.
+ * @param {Object} th The object that is (possibly) being jumped on.
+ * @param {Object} by The object doing the jumping-on.
+ * @returns True if the two objects are overlapping enough and by.accy>0.
+ */
+ isSquished:function(th,by) {
+ return ((by.accy>0)&&gbox.collides(th,by)&&(Math.abs(th.y-(by.y+by.h))<(th.h/2)))
+ },
+
+ /**
+ * Generates uniformly distributed random integers between min and min+range, non-inclusive. So help.random(0,2) will only return 0 and 1, etc.
+ * @param {Integer} min The minimum random value to be returned by the function.
+ * @param {Integer} range The number of different values returned by the function.
+ * @returns An integer between min (includive) and min+range (noninclusive).
+ */
+ random:function(min,range) {
+ return min+Math.floor(Math.random()*range);
+ },
+
+
+ /**
+ * Determines which frame of a given animation to display. Will loop an animation.
+ * @param {Integer} cnt A global frame counter.
+ * @param {Object} anim An object with parameters speed (the animation speed) and frames (the array representing the animation sequence).
+ * @returns The particular animation frame to display during this step.
+ */
+ decideFrame:function(cnt,anim) {
+ return anim.frames[Math.floor(cnt/anim.speed)%anim.frames.length];
+ },
+
+ /**
+ * Determines which frame of a given animation to display. Will remain on the last frame when the animation has played once.
+ * @param {Integer} cnt A global frame counter.
+ * @param {Object} anim An object with parameters speed (the animation speed) and frames (the array representing the animation sequence).
+ * @returns The particular animation frame to display during this step.
+ */
+ decideFrameOnce:function(cnt,anim) {
+ return anim.frames[(cnt>=anim.frames.length*anim.speed?anim.frames.length-1:Math.floor(cnt/anim.speed))];
+ },
+
+ /**
+ * Returns whether the animation was fully played at least once with decideFrame or fully with decideFrameOnce.
+ * @param {Integer} cnt A global frame counter.
+ * @param {Object} anim An object with parameters speed (the animation speed) and frames (the array representing the animation sequence).
+ * @returns A boolean, true if the animation has been played at least once.
+ */
+ isLastFrameOnce:function(cnt,anim) {
+ return (cnt>=anim.frames.length*anim.speed);
+ },
+
+ /**
+ * Given an incrementing value each step, this will return a value increasing from 0 until max/2, at which point it will decrement to 0, then go back up to max/2, in an endless cycle.
+ * @param {Integer} counter A counter.
+ * @param {Integer} max This determines the period of the function -- assuming counter is incrementing by one, a complete back-and-forth will take 'max' steps.
+ * @returns An integer.
+ */
+ upAndDown:function(counter,max) {
+ if ((counter%max)>(max/2)) return max-(counter%max); else return (counter%max);
+ },
+
+ /**
+ * Given x,y coordinates and map information, this returns the tile at a given point.
+ * @param {Integer} x An x-coordinate.
+ * @param {Integer} y A y-coordinate.
+ * @param {Object} map The map object.
+ * @param {Object} ifout An object or value to be returned if the x,y coordinate pair is outside the map.
+ * @param {String} mapid The id for the map array within the map object. Default is 'map'.
+ * @returns An integer representing the value of the tile in the map array at that x,y coordinate. If there is no tile, null is returned.
+ */
+ getTileInMap:function(x,y,map,ifout,mapid) {
+ if (!mapid) mapid="map";
+ var ts=gbox._tiles[map.tileset];
+ var tx=Math.floor(x/ts.tilew);
+ var ty=Math.floor(y/ts.tileh);
+ if ((ty<0)||(ty>=map[mapid].length)) return ifout; else
+ if ((tx<0)||(tx>=map[mapid][ty].length)) return ifout; else
+ return map[mapid][ty][tx];
+ },
+
+ /**
+ * Takes an ascii-art-style array of characters and converts it to an Akihabara-compatible map format.
+ * @param {Array} map An array of characters representing a map.
+ * @param {Array} tra A translation array. This is an array of arrays, formatted like [ [null, char1], [0, char2], [1, char3] ]. There must at least be a null entry, followed by one numerical entry for each tile type you want to render, corresponding to the unique characters in the map array. The null entry maps a character to empty space.
+ * @returns A map array formatted such that it can be attached to a map object.
+ */
+ asciiArtToMap:function(map,tra) {
+ var sz=tra[0][1].length;
+ var ret=[];
+ var xpos;
+ var pie;
+ for (var y=0;ymax.
+ */
+ limit:function(v,min,max) {
+ if (vmax) return max; else return v;
+ },
+
+ /**
+ * Subtracts or adds 1 to a value, always converging to zero. For example, passing -3 yields -2, 5 yields 4, etc. Works best with integers.
+ * @param {Integer} v A value.
+ * @returns A value that is one closer to 0 on the number line than v.
+ */
+ goToZero:function(v) { return (v?v-(v/Math.abs(v)):0); },
+
+ /**
+ * Merges two sets of parameters together without overwriting existing parameters. This merges from model to data, and if data and model share parameters, data's values remain intact.
+ * @param {Object} data An object containing a set of parameters, the destination of the merge.
+ * @param {Object} model An object containing a set of parameters, the source of the merge.
+ * @returns A merged model where the values of 'data' remain untouched: only new parameters and values from 'model' make it in.
+ * @example
+ * dst = {a: 1, b: 2, c: "three"};
+ * src = {c: "three", d: "four"};
+ * merged = help.mergeWithModel(dst,src);
+ * merged; // => {a: 1, b: 2, c: 3, d: "four"};
+ */
+ mergeWithModel:function(data,model) {
+ if (data==null) data={};
+ if (model!=null)
+ for (var i in model)
+ if (data[i]==null) data[i]=model[i];
+ return data;
+ },
+
+ /**
+ * Merges two sets of parameters together overwriting any existing parameters. This merges model->data, and if data and model share parameters, data's are overwritten by model's.
+ * @param {Object} data An object containing a set of parameters, the destination of the merge.
+ * @param {Object} model An object containing a set of parameters, the source of the merge.
+ * @returns A merged model where the values of 'model' take precedence over those of 'data'. The 'data' object is returned and will be an exact copy of 'model', plus any parameters that 'data' had before the merge that 'model' did not.
+ * @example
+ * dst = {a: 1, b: 2, c: "three"};
+ * src = {c: "three", d: "four"};
+ * merged = help.mergeWithModel(dst,src);
+ * merged; // => {a: 1, b: 2, c: "three", d: "four"}
+ */
+ copyModel:function(data,model) {
+ if (data==null) data={};
+ if (model!=null)
+ for (var i in model) data[i]=model[i];
+ return data;
+ },
+
+ /**
+ * Creates a subset of an existing set of parameters.
+ * @param {Object} obj An object containing a set of parameters, the source of the data.
+ * @param {Array} attrs An array of strings, containing the names of parameters you wish to copy.
+ * @returns A new set of parameters based on the subset specified.
+ * @example
+ * data = {a: 1, b: 2, c: "three"};
+ * newdata = help.createModel(data, ["a", "c"]);
+ * newdata; // => {a: 1, c: "three"}
+ */
+ createModel:function(obj,attrs) {
+ var ret={};
+ for (var i=0;i {a: 1, b: 2, c: "three"}
+ */
+ cloneObject:function(model) {
+ if (!model) return model;
+ var data={};
+ for (var i in model) data[i]=model[i];
+ return data;
+ },
+
+ /**
+ * Sets a tile in the map and draws it. Does not return anything.
+ * @param {Object} ctx The canvas context for the map. Accessed via gbox.getCanvasContext("canvasname")
+ * @param {Object} map The game map object.
+ * @param {Integer} x The index of the tile column within the map array -- so a 1 would mean the second column of tiles.
+ * @param {Integer} y The index of the tile row within the map array -- so a 1 would mean the second row of tiles.
+ * @param {Integer} tile The integer representing the new tile you wish to draw. This is its index within the tileset; a null value will erase whatever tile is present.
+ * @param {String} The ID of the map. Defaults to 'map'.
+ * @example
+ * // Remove the second tile to the right and down from the upper left corner of the tile map. Assumes our map canvas is called 'map_canvas'.
+ * help.setTileInMap(gbox.getCanvasContext("map_canvas"),map,1,1,null,"map");
+ */
+ setTileInMap:function(ctx,tilemap,x,y,tile,map) {
+ var ts=gbox.getTiles(tilemap.tileset);
+ tilemap[(map==null?"map":map)][y][x]=tile;
+ if (tile==null)
+ gbox.blitClear(ctx,{x:x*ts.tilew,y:y*ts.tilew,h:ts.tileh,w:ts.tilew});
+ else
+ gbox.blitTile(ctx,{tileset:tilemap.tileset,tile:tile,dx:x*ts.tilew,dy:y*ts.tilew});
+ },
+
+ /**
+ * Sets a tile in the map and draws it using pixels as coords. Does not return anything.
+ * @param {Object} ctx The canvas context for the map. Accessed via gbox.getCanvasContext("canvasname")
+ * @param {Object} map The game map object.
+ * @param {Integer} x The index of the pixel column within the map array -- so a 1 would mean the second column of tiles.
+ * @param {Integer} y The index of the pixel row within the map array -- so a 1 would mean the second row of tiles.
+ * @param {Integer} tile The integer representing the new tile you wish to draw. This is its index within the tileset; a null value will erase whatever tile is present.
+ * @param {String} The ID of the map. Defaults to 'map'.
+ */
+ setTileInMapAtPixel:function(ctx,tilemap,x,y,tile,map) {
+ var ts=gbox.getTiles(tilemap.tileset);
+ x=Math.floor(x/ts.tilew);
+ y=Math.floor(y/ts.tileh);
+ help.setTileInMap(ctx,tilemap,x,y,tile,map);
+ },
+
+
+ /**
+ * Returns the Nth element in an array. If the array is shorter than N, it returns the last element of the array.
+ * @param {Array} a An array.
+ * @param {Integer} id An index to the array.
+ * @returns If id > a.length, it returns a[a.length-1]. Otherwise returns a[id].
+ */
+ getArrayCapped:function(a,id) {
+ if (id>=a.length) return a[a.length-1]; else return a[id];
+ },
+
+ /**
+ * Returns the element of a sorted array that have the highest value of one of the properties.
+ * @param {Array} a An array.
+ * @param {Integer} value The target value.
+ * @param {String} field The property used to filter the array.
+ * @returns The object with the highest target value, otherwise the first element of the array.
+ */
+ getArrayIndexed:function(a,value,field) {
+ if (a[0][field]==null) return a[0];
+ var i=0;
+ while ((value>a[i][field])&&(i!=a.length-1)) i++;
+ return a[i];
+ },
+
+
+ /**
+ * Converts a quantity of frames into a timestamp formatted "mm:ss:cs" (minutes, seconds, centiseconds). Calculated using the current frames per second.
+ * @param {Integer} frames A quantity of frames.
+ * @returns A string containing a timestamp formatted "mm:ss:cs", representing the length of time it would take to render that many frames.
+ * @example
+ * // Assuming 25 frames per second, Akihabara's default.
+ * timestamp = help.framestotime(25);
+ * timestamp; // => '00:01:00';
+ * timestamp = help.framestotime(25 * 60);
+ * timestamp; // => '01:00:00';
+ */
+ framestotime:function(frames) {
+ var csec=Math.ceil(frames/gbox.getFps()*100);
+ return this.prepad((Math.floor(csec/6000)%60),2,"0")+":"+this.prepad((Math.floor(csec/100)%60),2,"0")+":"+this.prepad(csec%100,2,"0");
+
+ },
+
+ /**
+ * Reads the value of a query parameter from the URL of the web page.
+ * @param {String} name The name of the URL parameter.
+ * @returns The value of the URL parameter, as a string.
+ * @example
+ * // If the URL is http://example.com/game.html?lives=3
+ * player.lives = help.geturlparameter("lives");
+ * player.lives; // => 3
+ */
+ geturlparameter:function( name ) {
+ name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
+ var regexS = "[\\?&]"+name+"=([^]*)";
+ var regex = new RegExp( regexS );
+ var results = regex.exec( window.location.href );
+ if( results == null )
+ return "";
+ else
+ return results[1];
+ },
+
+ /**
+ * Writes the contents of an object to a string. Useful for debugging.
+ * @param {Object} Any object.
+ * @returns A string containing all the contents of an object. If the object contains functions, the string will contain the code for those functions.
+ */
+ objToStr:function(o) {
+ var ret="";
+ for (var n in o) ret+=n+":["+o[n]+"] ";
+ return ret;
+ },
+
+ /**
+ * Tests whether an object contains a given parameter.
+ * @param {Object} A reference to a parameter of an object.
+ * @returns True if the object contains that parameter, false if it does not.
+ * @example
+ * foo = {a: 1, b: 2};
+ * help.isDefined(foo.a); // => true
+ * help.isDefined(foo.c); // => false
+ */
+ isDefined:function(v) {
+ return ((typeof(v) !== 'undefined') || (v===null));
+ },
+
+ /**
+ * Automatically configures a bunch of settings depending on the web browser and device that is viewing the game. Mostly sets the maximum number of audio channels and touch settings.
+ */
+ getDeviceConfig:function() {
+ var cap;
+ if (navigator.userAgent.match(/nintendo wii/i))
+ cap={iswii:true,height:window.innerHeight,doublebuffering:true} // Simulated double buffering has been resumed. Canvas on Opera for Wii has a strange sprite blinking effect - usually browsers render frames once ended and this is an exception.
+ else if (navigator.userAgent.match(/iPhone/i)||navigator.userAgent.match(/iPod/i) || navigator.userAgent.match(/Android/i))
+ cap={touch:true,width:320};
+ else if (navigator.userAgent.match(/iPad/i))
+ cap={touch:true,width:768,forcedidle:10}; // Forced idle time is needed for correct framerate calculation.
+ else
+ cap={zoom:2};
+
+ cap.canaudio=!!(document.createElement('audio').canPlayType);
+ if (cap.canaudio) {
+ if (navigator.userAgent.match(/iPad/i)||navigator.userAgent.match(/iPhone/i)||navigator.userAgent.match(/iPod/i)) {
+ cap.audiocompatmode=2; // Audio loading mode.
+ cap.audioteam=1; // Only a member is required in the audioteam.
+ cap.audioisexperimental=true; // Audio is experimental, since limited.
+ cap.audioissinglechannel=true; // Single channeled. Plays only the "bgmusic" channel.
+ } else if (navigator.userAgent.match(/Chrome/i)) {
+ cap.audioteam=3; // Quite low performance on playback responsiveness.
+ } else if (navigator.userAgent.match(/Firefox/i)) {
+ cap.audioteam=1; // Testing smaller audioteam
+ cap.audiopositiondelay=0.3; // Ogg playback is slower 0.3 between MP3 playback. Don't know why :)
+ cap.audiocreatemode=1; // Firefox is stalling while downloading lot of things
+ } else if (navigator.userAgent.match(/Minefield/i)) {
+ cap.audioteam=1; // Testing smaller audioteam
+ cap.audiocreatemode=1; // Firefox is stalling while downloading lot of things
+ // Minefield has fixed the 0.3 delay!
+ } else if (navigator.userAgent.match(/khtml/i)&&navigator.userAgent.match(/konqueror/i)) {
+ // Note that audio is not supported in applewebkit mode :(
+ cap.audioteam=1;
+ cap.audioissinglechannel=true; // Single channeled. Plays only the "bgmusic" channel.
+ cap.audiocompatmode=2; // Sorry. iPad single channel mode. Audio events are not triggered properly and audio properties are missing so many audio feautres are not available. :(
+ cap.forcedmimeaudio="audio/ogg"; // Usually OGG audio playback is supported by default in KDE env.
+ cap.audioisexperimental=true; // Audio is experimental, since limited.
+ } else if (navigator.userAgent.match(/Safari/i)) {
+ cap.audioteam=1; // Testing smaller audioteam
+ } else if (navigator.userAgent.match(/Opera/i)) {
+ cap.audioteam=1; // Testing smaller audioteam
+ cap.audiocreatemode=1; // Do not like audio object cloning very much
+ } else if (navigator.userAgent.match(/MSIE 9.0/i)||navigator.userAgent.match(/MSIE 7.0/i)) {
+ cap.audioteam=2;
+ cap.audiocompatmode=1; // Audio loading mode.
+ cap.audioisexperimental=navigator.userAgent.match(/MSIE 7.0/i); // Audio is experimental, on IE7 (for compat mode)
+ } else
+ cap.audioisexperimental=true; // Audio is just experimental on all other devices.
+ }
+ return cap;
+ },
+
+ /**
+ * This provides a number of configurations: fps, display zoom, dynamic frameskip, force touch parameters, etc. Many of these settings can
+ * be set manually by passing an object with the parameters defined, or via URL parameters.
+ * @param {Object} data An optional object containing parameters you wish to set. Works for data.zoom, data.splash, data.width, data.height, data.title, data.fps, and data.padmode.
+ */
+ akihabaraInit:function(data) {
+ if ((typeof data).toLowerCase() == "string") data={title:data};
+ var device=this.getDeviceConfig();
+ var footnotes=["MADE WITH AKIHABARA (C)2010 - GPL2/MIT","Project: www.kesiev.com/akihabara","Sources: github.com/kesiev/akihabara"];
+ document.title=(data.title?data.title:"Akihabara");
+ if (data.splash) {
+ if (data.splash.footnotes)
+ for (var i=0;iiphopad._width-iphopad._buttonsize)
+ nb.a=true;
+ else if (rp.x>iphopad._width-iphopad._buttonsize2)
+ nb.b=true;
+ else if (rp.x>iphopad._width-iphopad._buttonsize3)
+ nb.c=true;
+
+ }
+ this._swap=!this._swap;
+ for (var i in this._cross) {
+ if (nc[i]!=iphopad._cross[i])
+ if (nc[i]) gbox._keydown({fake:true,keyCode:gbox._keymap[i]});
+ else gbox._keyup({fake:true,keyCode:gbox._keymap[i]});
+ }
+ for (var i in this._buttons) {
+ if (nb[i]!=iphopad._buttons[i])
+ if (nb[i]) gbox._keydown({fake:true,keyCode:gbox._keymap[i]});
+ else gbox._keyup({fake:true,keyCode:gbox._keymap[i]});
+ }
+
+ iphopad._cross=nc;
+ iphopad._buttons=nb;
+ },
+ _fakelisten:function(e) {
+ iphopad._listen({
+ touches:[
+ {
+ pageX:e.clientX,
+ pageY:e.clientY
+ }
+ ]
+ });
+ },
+
+ /**
+ * Initializes the game controls for use with an I-product or Android device.
+ * @param {Object} data passes in information about the screen and its traits such as size.
+ */
+ initialize:function(data) {
+ var oElement=document.createElement("div");
+ oElement.style.margin="auto";
+ oElement.style.padding="0px";
+ oElement.style.height=data.h+"px";
+ oElement.style.width="100%";
+ oElement.style.backgroundImage="url("+data.bg+")";
+ oElement.style.backgroundRepeat="repeat-x";
+
+ var tpad=document.createElement("div");
+ tpad.style.cssFloat="left";
+ tpad.style.padding="0px";
+ tpad.style.margin="0px";
+ tpad.style.height=data.h+"px";
+ tpad.style.width=data.h+"px";
+ tpad.style.backgroundImage="url("+data.dpad+")";
+ tpad.style.backgroundRepeat="no-repeat";
+
+ var bpad=document.createElement("div");
+ bpad.style.cssFloat="right";
+ bpad.style.padding="0px";
+ bpad.style.margin="0px";
+ bpad.style.height=data.h+"px";
+ bpad.style.width=iphopad._buttonsize3+"px";
+ bpad.style.backgroundImage="url("+data.buttons+")";
+ bpad.style.backgroundRepeat="no-repeat";
+
+ oElement.appendChild(tpad);
+ oElement.appendChild(bpad);
+ gbox._box.appendChild(oElement);
+
+ oElement.ontouchstart=function(evt) { evt.preventDefault();evt.stopPropagation(); iphopad._listen(evt) };
+ oElement.ontouchend=function(evt) { evt.preventDefault();evt.stopPropagation();iphopad._listen(evt) };
+ oElement.ontouchmove=function(evt) { evt.preventDefault();evt.stopPropagation();iphopad._listen(evt) };
+ //oElement.onmousemove=function(evt) { iphopad._fakelisten(evt) };
+ var sizes=gbox._domgetabsposition(oElement);
+ this._gapx=sizes.x;
+ this._gapy=sizes.y;
+ this._width=sizes.w;
+ this._height=sizes.h;
+ this._center={x:Math.floor(this._height/2),y:Math.floor(this._height/2)};
+ }
+
+}
diff --git a/public/akihabara/padbg.png b/public/akihabara/padbg.png
new file mode 100644
index 0000000..09b9db0
Binary files /dev/null and b/public/akihabara/padbg.png differ
diff --git a/public/akihabara/splash.png b/public/akihabara/splash.png
new file mode 100644
index 0000000..5d031ce
Binary files /dev/null and b/public/akihabara/splash.png differ
diff --git a/public/akihabara/tool.js b/public/akihabara/tool.js
new file mode 100644
index 0000000..9f41b2e
--- /dev/null
+++ b/public/akihabara/tool.js
@@ -0,0 +1,131 @@
+// ---
+// Copyright (c) 2010 Francesco Cottone, http://www.kesiev.com/
+// ---
+
+/**
+ * @namespace Tool module provides simple developing tools. Currently, this file only has a cel-composer:
+ * it can compose an image stacking a set of frames for animating objects, applying a
+ * number of filters to each frame.
+ */
+var tool={
+ _images:[],
+ _loadedflag:[],
+ _data:{},
+ _count:0,
+ _countloaded:0,
+
+ /**
+ * This function documents that an image in an animation sequence is loaded and checks if the other images are loaded or not
+ * @param {Object} id This is the object which is used as an id for keeping track of things related to this object in this function
+ */
+ _loaded:function(id) {
+ this._loadedflag[id]=true;
+ tool._countloaded++;
+ document.title=tool._countloaded+"/"+tool._count;
+ for (var i=0;i
+ *
x{Integer}: (required)Objects x position
+ *
y{Integer}: (required)Objects y position
+ *
z{Integer}: (required)Objects z position
+ *
colx{Integer}: (required)The dimension of the collision box along the x axis
+ *
coly{Integer}: (required)The dimension of the collision box along the y axis
+ *
colh{Integer}: (required)Collision box height
+ *
colw{Integer}: (required)Collision box width
+ *
+ * @param {Object} to The object that collision is being checked against.
+ *
+ *
x{Integer}: (required)Objects x position
+ *
y{Integer}: (required)Objects y position
+ *
z{Integer}: (required)Objects z position
+ *
colx{Integer}: (required)Collision x
+ *
coly{Integer}: (required)Collision y
+ *
colh{Integer}: (required)Collision box height
+ *
colw{Integer}: (required)Collision box width
+ *
+ * @param {int} t This is the tollerance (or margin for error) on the collide function.
+ */
+ collides:function(fr,to,t) { // Special collision. Counts also the Z
+ if (Math.abs(fr.z,to.z)<5) return gbox.collides({x:fr.x+fr.colx,y:fr.y+fr.coly,h:fr.colh,w:fr.colw},{x:to.x+to.colx,y:to.y+to.coly,h:to.colh,w:to.colw},t); else return false;
+ },
+
+ /**
+ * Checks for pixel collisions with an offset to the X and Y of the colidable using colx and coly.
+ * @param {Object} fr The object which collision is being tested for.
+ * @param {Object} to The object (or point) which collision is being tested against.
+ * @param {int} t The tollerance of the collision algorithm.
+ */
+ pixelcollides:function(fr,to,t) { // Special collision. Counts also the Z
+ return gbox.pixelcollides(fr,{x:to.x+to.colx,y:to.y+to.coly,h:to.colh,w:to.colw},t);
+ },
+
+ /**
+ * Initializes the game with the variables needed for topview and whatever else you feed in through data.
+ * @param {Object} th Passes in the object being initialized.
+ * @param {Object} data This is used to pass in everything that's being initiliized. If a value is not in Data then a default value is used instead. This can pass in values which do not have a default.
+ *
+ *
x{Integer}: x position of the object. (defaults to 0)
+ *
y{Integer}: y position of the object. (defaults to 0)
+ *
z{Integer}: z index of the object. (defaults to 0)
+ *
accx{Integer}: The starting x velociyt of the object. (defaults to 0)
+ *
accy{Integer}: The starting y velocity of the object. (defaults to 0)
+ *
accz{Integer}: The starting z velocity of the object. (defaults to 0)
+ *
frames{Object}: This is stores the animation frames for the objects in a map style structure. An empty map means the default image will display with no animation frames. (defaults to an empty map)
+ *
shadow: (defaults to null)
//incomplete
+ *
maxacc{Integer}: (defaults to )4
+ *
controlmaxacc{Integer}: (defaults to 4)
+ *
responsive: (defaults to 0)
+ *
weapon: (defaults to 0)
+ *
camera{Boolean}: (defaults to true)
+ *
flipv{Boolean}: Notes if the object is flipped vertically(defaults to false)
+ *
fliph{Boolean}: Notes if the object is flipped horrizontally(defaults to false)
+ *
facing{Integer}: Stores the facing of the object. This is set with pre-defined Integer values from within Toys.(defaults to toys.FACE_DOWN)
+ *
+ *
FACE_UP:0,
+ *
FACE_RIGHT:1,
+ *
FACE_DOWN:2,
+ *
FACE_LEFT:3,
+ *
+ *
flipside{Boolean}: (defaults to true)
+ *
haspushing{Boolean}: (defaults to false)
+ *
frame: (default to 0)
+ *
colh{Integer}: (defaults to gbox.getTiles(th.tileset).tilehh)
+ *
colw{Integer}: (defaults to gbox.getTiles(th.tileset).tilew)
+ *
colx{Integer}: (defaults to 0)
+ *
staticspeed{Integer}: (defaults to 0)
+ *
nodiagonals{Boolean}: (defaults to false)
+ *
noreset: (defaults to false)
+ *
+ */
+ initialize:function(th,data) {
+ help.mergeWithModel(
+ th,
+ help.mergeWithModel(
+ data,
+ {
+ x:0, y:0,
+ z:0,
+ accx:0, accy:0, accz:0,
+ frames:{},
+ shadow:null,
+ maxacc:4, controlmaxacc:4,
+ responsive:0, // Responsiveness
+ weapon:0, // Weapon
+ camera:true,
+ flipv:false, fliph:false,
+ facing:toys.FACE_DOWN,
+ flipside:true,
+ haspushing:false,
+ frame:0,
+ colh:gbox.getTiles(th.tileset).tilehh,
+ colw:gbox.getTiles(th.tileset).tilew,
+ colx:0,
+ staticspeed:0,
+ nodiagonals:false,
+ noreset:false
+ }
+ )
+ );
+ if (th.coly==null) th.coly=gbox.getTiles(th.tileset).tileh-th.colh;
+ th.colhh=Math.floor(th.colh/2);
+ th.colhw=Math.floor(th.colw/2);
+
+ toys.topview.spawn(th);
+ },
+
+ /**
+ * Spawns a new object in the topview namespace. This also merges parameters in data into paramaters in th using help.copyModel.
+ * This initializes some basic basic variables for the object and sets the Z index.
+ * @param {Object} th References 'this' which is the object that called the method (generally).
+ *
+ *
y {Integer}: (required) The object's y position.
+ *
h {Integer}: (required) The object's height.
+ *
+ * @param {Object} data This holds variables to be merged into th's stored info.
+ */
+ spawn:function(th,data) {
+ th.xpushing=toys.PUSH_NONE; // user is moving side
+ th.vpushing=toys.PUSH_NONE; // user is moving side
+ th.zpushing=toys.PUSH_NONE; // user is moving side
+ th.counter=0; // self counter
+ th.hittimer=0;
+ th.killed=false;
+ help.copyModel(th,data);
+ gbox.setZindex(th,th.y+th.h); // these object follows the z-index and uses ZINDEX_LAYER
+ },
+
+ /**
+ * This sets and runs the control keys for the game.
+ * @param {Object} th This is the object that is being controlled by the keys (assumed to be the player)
+ *
+ *
accx: the object's currect acceleration in the x direction
+ *
accy: the object's currect acceleration in the y direction
+ *
responsive: minimum movement speed
+ *
staticspeed: turns off acceleration
+ *
nodiagonals: boolean determining if the object can move along both axis at once.
+ *
xpushing: a boolean that notes whether the object is pushing against something in the x direction.
+ *
ypushing: a boolean that notes whether the object is pushing against something in the y direction.
+ *
controlmaxacc: max acceleration for the object along an axis
+ *
noreset: checks for the object being allowed to reset its pushing status (?)
+ *
+ * @param {Object} keys These are the control keys being passed in for left, right, up, and down.
+ * //incomplete
+ */
+ controlKeys:function(th,keys) {
+ var cancelx=false;
+ var cancely=false;
+ var idlex=false;
+ var idley=false;
+
+ if (gbox.keyIsPressed(keys.left)||keys.pressleft) {
+ th.xpushing=toys.PUSH_LEFT;
+ th.facing=toys.FACE_LEFT;
+ if (th.accx>th.responsive) th.accx=th.responsive;
+ if (th.staticspeed) th.accx=-th.staticspeed; else th.accx=help.limit(th.accx-1,-th.controlmaxacc,th.controlmaxacc);
+ if (th.nodiagonals) { cancely=true; idley=true }
+ } else if (gbox.keyIsPressed(keys.right)||keys.pressright) {
+ th.xpushing=toys.PUSH_RIGHT;
+ th.facing=toys.FACE_RIGHT;
+ if (th.accx<-th.responsive) th.accx=-th.responsive;
+ if (th.staticspeed) th.accx=th.staticspeed; else th.accx=help.limit(th.accx+1,-th.controlmaxacc,th.controlmaxacc);
+ if (th.nodiagonals) { cancely=true; idley=true }
+ } else idlex=true;
+
+ if (!cancely&&(gbox.keyIsPressed(keys.up)||keys.pressup)) {
+ th.ypushing=toys.PUSH_UP;
+ th.facing=toys.FACE_UP;
+ if (th.accy>th.responsive) th.accy=th.responsive;
+ if (th.staticspeed) th.accy=-th.staticspeed; else th.accy=help.limit(th.accy-1,-th.controlmaxacc,th.controlmaxacc);
+ if (th.nodiagonals) { cancelx=true; idlex=true; }
+ } else if (!cancely&&(gbox.keyIsPressed(keys.down)||keys.pressdown)) {
+ th.ypushing=toys.PUSH_DOWN;
+ th.facing=toys.FACE_DOWN;
+ if (th.accy<-th.responsive) th.accy=-th.responsive;
+ if (th.staticspeed) th.accy=th.staticspeed; else th.accy=help.limit(th.accy+1,-th.controlmaxacc,th.controlmaxacc);
+ if (th.nodiagonals) { cancelx=true; idlex=true; }
+ } else idley=true;
+
+
+
+ if (idlex) {
+ if (cancelx) th.accx=0;
+ if (cancelx||!th.noreset) th.xpushing=toys.PUSH_NONE;
+ }
+ if (idley) {
+ if (cancely) th.accy=0;
+ if (cancely||!th.noreset) th.ypushing=toys.PUSH_NONE;
+ }
+ },
+
+ /**
+ * Gets the next X position the object is going to move to.
+ * @param {Object} th The object being checked.
+ *
+ *
x: the current x position of the object
+ *
accx: the object's currect acceleration in the x direction
+ *
maxacc: the max accleration the object can have (if accx is greater than this then this value is used instead)
+ *
+ */
+ getNextX:function(th) { return th.x+help.limit(th.accx,-th.maxacc,th.maxacc); },
+
+ /**
+ * Gets the next Y position the object is going to move to.
+ * @param {Object} th The object being checked.
+ *
+ *
y: the current y position of the object
+ *
accy: the object's currect acceleration in the y direction
+ *
maxacc: the max accleration the object can have (if accy is greater than this then this value is used instead)
+ *
+ */
+ getNextY:function(th) { return th.y+help.limit(th.accy,-th.maxacc,th.maxacc); },
+
+ /**
+ * Gets the next Z position the object is going to move to.
+ * @param {Object} th The object being checked.
+ *
+ *
z: the current z position of the object
+ *
accz: the object's currect acceleration in the z direction
+ *
maxacc: the max accleration the object can have (if accz is greater than this then this value is used instead)
+ *
+ */
+ getNextZ:function(th) { return th.z+help.limit(th.accz,-th.maxacc,th.maxacc); },
+
+ /**
+ * Sets the objects current location to its next location using the getNextX and getNextY methods.
+ * @param {Object} th The object being modified.
+ *
+ *
x: the current x position of the object
+ *
y: the current y position of the object
+ *
accx: the object's currect acceleration in the x direction
+ *
accy: the object's currect acceleration in the y direction
+ *
maxacc: the max accleration the object can have (if either acceleration is greater than this then this value is used instead for that acceleration)
+ *
+ */
+ applyForces:function(th) {
+ th.x=toys.topview.getNextX(th);
+ th.y=toys.topview.getNextY(th);
+ },
+
+ /**
+ * This applies acceleration in the Z direction (not nessesarily gravity but whatever the next accerlation on the Z axis is)
+ * @param {Object} th The object being modified.
+ *
+ *
z: the current z position of the object
+ *
accz: the object's currect acceleration in the z direction
+ *
maxacc: the max accleration the object can have (if accz is greater than this then this value is used instead)
+ *
+ */
+ applyGravity:function(th) {
+ th.z=toys.topview.getNextZ(th);
+ },
+
+ /**
+ * Degrades all accelerations on an object by one toward zero.
+ * @param {Object} th The object being modified.
+ *
+ *
xpushing: a boolean that notes whether the object is pushing against something in the x direction.
+ *
ypushing: a boolean that notes whether the object is pushing against something in the y direction.
+ *
accx: the object's currect acceleration in the x direction
+ *
accy: the object's currect acceleration in the y direction
+ *
+ */
+ handleAccellerations:function(th) {
+ if (!th.xpushing) th.accx=help.goToZero(th.accx);
+ if (!th.ypushing) th.accy=help.goToZero(th.accy);
+
+ },
+
+ /**
+ * Increases the Z acceleration on the object by one.
+ * @param {Object} th The object being modified.
+ *
+ *
accz: the acceleration on the Z axis
+ *
+ */
+ handleGravity:function(th) {
+ th.accz++;
+ },
+
+ /**
+ * This sets which frame the object is going to display based on an agregate word that describes predefined states.
+ * @param {Object} th The object whose frame is being set.
+ *
+ *
xpushing: a boolean that notes whether the object is pushing against something in the x direction.
+ *
ypushing: a boolean that notes whether the object is pushing against something in the y direction.
+ *
haspushing: a boolean that notes if the object changes when pushing against something.
+ *
toucheddown: a boolean that notes if the object is touching something below it on the screen.
+ *
touchedup: a boolean that notes if the object is touching something above it on the screen.<
+ *
touchedright: a boolean that notes if the object is touching something right of it on the screen.<
+ *
touchedleft: a boolean that notes if the object is touching something left of it on the screen.<
+ *
flipside:
+ *
fliph:
+ *
facing:
+ *
frames:
+ *
frame:
+ *
counter:
+ *
+ * // incomplete
+ */
+ setFrame:function(th) {
+ var pref="stand";
+ if (th.xpushing||th.ypushing)
+ if (th.haspushing&&(th.toucheddown||th.touchedup||th.touchedleft||th.touchedright)) pref="pushing"; else pref="moving";
+ if (th.flipside)
+ th.fliph=(th.facing==toys.FACE_RIGHT);
+ th.frame=help.decideFrame(th.counter,th.frames[pref+toys.FACES[th.facing]]);
+ },
+
+ /**
+ * Checks if the specified object is colliding with tiles in the map in an area defined by the object's colw and colh variables as well as the tolerance and approximation variables that are passed in through data. Only tiles in the map marked as solid are checked against. The alogrithm checks the
+ * @param {Object} th The object that is being checked against the tilemap.
+ * @param {Object} map This is the asci map that the tile map is generated from.
+ * @param {Object} tilemap This is the array of tile objects that it itterated over checking for collisions.
+ * @param {Object} defaulttile The default tile to be returned if nothing can be found. Null can be used here.
+ * @param {Object} data Passes is extra dat to the function. Can be set as null.
+ *
+ *
tolerance{Integer}: This is subtracted from the collision space to get the maximum collision area for the object. This defaults to 6.
+ *
approximation{Integer}: This is the amount that the checked values are incremented by until they reach the maximum value allowed. This defaults to 10.
+ *
+ */
+ tileCollision:function(th,map,tilemap,defaulttile,data) {
+
+ th.touchedup=false;
+ th.toucheddown=false;
+ th.touchedleft=false;
+ th.touchedright=false;
+
+ var tolerance=(data&&(data.tolerance!=null)?data.tolerance:6);
+ var approximation=(data&&(data.approximation!=null)?data.approximation:10);
+ var t=tolerance-approximation;
+ do {
+ t+=approximation;
+ if (t>th.colw-tolerance-1) t=th.colw-tolerance-1;
+ var bottom=help.getTileInMap(th.x+th.colx+t,th.y+th.coly+th.colh-1,map,defaulttile,tilemap);
+ var top=help.getTileInMap(th.x+th.colx+t,th.y+th.coly,map,defaulttile,tilemap);
+ if (map.tileIsSolid(th,top)) th.touchedup=true;
+ if (map.tileIsSolid(th,bottom)) th.toucheddown=true;
+ } while (t!=th.colw-tolerance-1);
+
+ t=tolerance-approximation;
+ do {
+ t+=approximation;
+ if (t>th.colh-tolerance-1) t=th.colh-tolerance-1;
+ var left=help.getTileInMap(th.x+th.colx,th.y+th.coly+t,map,defaulttile,tilemap);
+ var right=help.getTileInMap(th.x+th.colx+th.colw-1,th.y+th.coly+t,map,defaulttile,tilemap);
+ if (map.tileIsSolid(th,left)) th.touchedleft=true;
+ if (map.tileIsSolid(th,right)) th.touchedright=true;
+ } while (t!=th.colh-tolerance-1);
+
+ if (th.touchedup) {
+ th.accy=0;
+ th.y=help.yPixelToTile(map,th.y+th.coly,1)-th.coly;
+ }
+ if (th.toucheddown) {
+ th.accy=0;
+ th.y=help.yPixelToTile(map,th.y+th.coly+th.colh-1)-th.coly-th.colh;
+ }
+ if (th.touchedleft) {
+ th.accx=0;
+ th.x=help.xPixelToTile(map,th.x+th.colx,1)-th.colx;
+ }
+ if (th.touchedright) {
+ th.accx=0;
+ th.x=help.xPixelToTile(map,th.x+th.colx+th.colw-1)-th.colx-th.colw;
+ }
+
+ },
+
+ /**
+ * @param {Object} th The object being checked for collisions.
+ *
+ *
+ *
+ *
+ *
+ *
+ * @param {Object} data This is used to pass in other data and arguments.
+ *
+ *
group {String}: (required) This is the group of objects being checked against.
+ *
+ *
+ *
+ *
+ *
//incomplete
+ */
+ spritewallCollision:function(th,data) {
+ var wl;
+ for (var i in gbox._objects[data.group])
+ if ((!gbox._objects[data.group][i].initialize)&&toys.topview.collides(th,gbox._objects[data.group][i])) {
+ wl=gbox._objects[data.group][i];
+ if (toys.topview.pixelcollides({x:th.x+th.colx,y:th.y+th.coly+th.colhh},wl)) {
+ th.touchedleft=true;
+ th.accx=0;
+ th.x=wl.x+wl.colx+wl.colw-th.colx;
+ } else if (toys.topview.pixelcollides({x:th.x+th.colx+th.colw,y:th.y+th.coly+th.colhh},wl)) {
+ th.touchedright=true;
+ th.accx=0;
+ th.x=wl.x+wl.colx-th.colw-th.colx;
+ }
+ if (toys.topview.pixelcollides({x:th.x+th.colx+th.colhw,y:th.y+th.coly+th.colh},wl)) {
+ th.toucheddown=true;
+ th.accy=0;
+ th.y=wl.y+wl.coly-th.colh-th.coly;
+ } else if (toys.topview.pixelcollides({x:th.x+th.colx+th.colhw,y:th.y+th.coly},wl)) {
+ th.touchedup=true;
+ th.accy=0;
+ th.y=wl.y+wl.coly+wl.colh-th.coly;
+ }
+ }
+
+ },
+
+ /**
+ * This checks if the object's z index is 0 which means it has hit the floor. If this has occured it also plays an impact or bounce noise if one is passed in. Note: The area above the floor is in the negative z index space so a value of 1 for z will return that the object has collided with the floor and z will then be set to zero.
+ * @param {Object} th The object being checked for collision.
+ *
+ *
touchedfloor{boolean}: This value is not passed in but is created or set in the function. This contains the function's return value.
+ *
+ *
+ *
+ *
+ * @param {Object} data This is used to pass in extra parameters.
+ *
+ *
+ *
+ */
+ floorCollision:function(th,data) {
+ th.touchedfloor=false;
+ if (th.z>0) {
+ th.accz=(data==null?0:-Math.floor(th.accz/data.bounce));
+ if (data&&data.audiobounce&&th.accz) gbox.hitAudio(data.audiobounce);
+ th.z=0;
+ th.touchedfloor=true;
+ }
+ },
+
+ /**
+ *
+ */
+ adjustZindex:function(th) {
+ gbox.setZindex(th,th.y+th.h);
+ },
+
+ /**
+ *
+ */
+ // Helper: returns the ahead pixel (i.e. destination use action)
+ getAheadPixel:function(th,data) {
+ switch (th.facing) {
+ case toys.FACE_RIGHT:{
+ return {x:th.x+th.colx+th.colw+data.distance,y:th.y+th.coly+th.colhh};
+ break;
+ }
+ case toys.FACE_LEFT:{
+ return {x:th.x+th.colx-data.distance,y:th.y+th.coly+th.colhh};
+ break;
+ }
+ case toys.FACE_UP:{
+ return {x:th.x+th.colx+th.colhw,y:th.y+th.coly-data.distance};
+ break;
+ }
+ case toys.FACE_DOWN:{
+ return {x:th.x+th.colx+th.colhw,y:th.y+th.coly+th.colh+data.distance};
+ break;
+ }
+ }
+ },
+
+ /**
+ *
+ */
+ // Helper: trigger a method in colliding objects (i.e. "use action")
+ callInColliding:function(th,data) {
+ for (var i in gbox._objects[data.group])
+ if ((!gbox._objects[data.group][i].initialize)&&toys.topview.pixelcollides(data,gbox._objects[data.group][i]))
+ if (gbox._objects[data.group][i][data.call]) {
+ gbox._objects[data.group][i][data.call](th);
+ return i;
+ }
+ return false;
+ },
+
+ /**
+ *
+ */
+ // Enemy methods
+ wander:function(th,map,tilemap,defaulttile,data) {
+ if ((!th.wandercounter)||(th.toucheddown||th.touchedup||th.touchedleft||th.touchedright)) {
+ th.wandercounter=help.random(data.minstep,data.steprange);
+ th.wanderdirection=help.random(0,4);
+ } else th.wandercounter--;
+ switch (th.wanderdirection) {
+ case toys.FACE_LEFT: {
+ th.xpushing=toys.PUSH_LEFT;
+ th.ypushing=toys.PUSH_NONE;
+ th.facing=toys.FACE_LEFT;
+ th.accx=-data.speed;
+ th.accy=0;
+ break;
+ }
+ case toys.FACE_RIGHT: {
+ th.xpushing=toys.PUSH_RIGHT;
+ th.ypushing=toys.PUSH_NONE;
+ th.facing=toys.FACE_RIGHT;
+ th.accx=data.speed;
+ th.accy=0;
+ break;
+ }
+ case toys.FACE_UP: {
+ th.ypushing=toys.PUSH_UP;
+ th.xpushing=toys.PUSH_NONE;
+ th.facing=toys.FACE_UP;
+ th.accy=-data.speed;
+ th.accx=0;
+ break;
+ }
+ case toys.FACE_DOWN: {
+ th.ypushing=toys.PUSH_DOWN;
+ th.xpushing=toys.PUSH_NONE;
+ th.facing=toys.FACE_DOWN;
+ th.accy=data.speed;
+ th.accx=0;
+ break;
+ }
+ }
+ },
+
+ /**
+ *
+ */
+ // generators (firebullet specific for topdown - more complex than SHMUP one)
+ fireBullet:function(gr,id,data) {
+
+ var ts=gbox.getTiles(data.tileset);
+
+
+ var obj=gbox.addObject(
+ help.mergeWithModel(
+ data,{
+ _bullet:true,
+ zindex:0,
+ fliph:false, flipv:false,
+ id:id,
+ group:gr,
+ cnt:0,
+ tileset:"",
+ frames:{},
+ acc:0,
+ angle:0,
+ camera:data.from.camera,
+ accx:(data.accx==null?Math.floor(trigo.translateX(0,data.angle,data.acc)):0),
+ accy:(data.accy==null?Math.floor(trigo.translateY(0,data.angle,data.acc)):0),
+ accz:0,
+ x:(data.sidex==toys.FACE_LEFT?data.from.x-ts.tilehw:(data.sidex==toys.FACE_RIGHT?data.from.x+data.from.w-ts.tilehw:data.from.x+data.from.hw-ts.tilehw))+(data.gapx?data.gapx:0),
+ y:(data.sidey==toys.FACE_UP?data.from.y-ts.tilehh:(data.sidey==toys.FACE_DOWN?data.from.y+data.from.h-ts.tilehh:data.from.y+data.from.hh-ts.tilehh))+(data.gapy?data.gapy:0),
+ z:(data.from.z==null?0:data.from.z),
+ collidegroup:"",
+ spark:toys.NOOP,
+ power:1,
+ lifetime:null,
+ tilemap:null,
+ defaulttile:0,
+ applyzgravity:false,
+ map:null,
+ defaulttile:0,
+ mapindex:"",
+ spritewalls:null,
+ colx:(data.fullhit?0:null),
+ coly:(data.fullhit?0:null),
+ colh:(data.fullhit?ts.tileh:null),
+ colw:(data.fullhit?ts.tilew:null),
+ duration:null,
+ onWallHit:function() {
+ this.spark(this);
+ gbox.trashObject(this);
+ },
+ bulletIsAlive:function() {
+ return gbox.objectIsVisible(this);
+ }
+ }
+ )
+ );
+
+ obj.initialize=function() {
+ toys.topview.initialize(this);
+ };
+
+ obj[(data.logicon==null?"first":data.logicon)]=function() {
+ this.cnt=(this.cnt+1)%10;
+
+ if (this.applyzgravity) toys.topview.handleGravity(this); // z-gravity
+ toys.topview.applyForces(this); // Apply forces
+ if (this.applyzgravity) toys.topview.applyGravity(this); // z-gravity
+ if (this.map!=null) toys.topview.tileCollision(this,this.map,this.mapindex,this.defaulttile); // tile collisions
+ if (this.spritewalls!=null) toys.topview.spritewallCollision(this,{group:this.spritewalls}); // walls collisions
+ if (this.applyzgravity) toys.topview.floorCollision(this); // Collision with the floor (for z-gravity)
+ toys.topview.adjustZindex(this);
+ if (this.duration!=null) {
+ this.duration--;
+ if (this.duration==0) gbox.trashObject(this);
+ }
+ if (!this.bulletIsAlive()) gbox.trashObject(this);
+ else if (this.toucheddown||this.touchedup||this.touchedleft||this.touchedright) this.onWallHit();
+ else if (this.collidegroup!=null)
+ for (var i in gbox._objects[this.collidegroup])
+ if ((!gbox._objects[this.collidegroup][i].initialize)&&toys.topview.collides(this,gbox._objects[this.collidegroup][i],gbox._objects[this.collidegroup][i].tolerance)) {
+ if (gbox._objects[this.collidegroup][i]["hitByBullet"]!=null)
+ if (!gbox._objects[this.collidegroup][i].hitByBullet(this)) {
+ this.spark(this);
+ gbox.trashObject(this);
+ }
+ }
+ }
+
+ obj[(data.bliton==null?"blit":data.bliton)]=function() {
+ if (!gbox.objectIsTrash(this))
+ gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.cnt,this.frames),dx:this.x,dy:this.y+this.z,camera:this.camera,fliph:this.fliph,flipv:this.flipv});
+ }
+
+ gbox.setZindex(obj,obj.y+obj.h);
+
+ return obj;
+
+ },
+
+ /**
+ *
+ */
+ makedoor:function(gr,id,map,data) {
+
+ var mts=gbox.getTiles(map.tileset);
+ var ts=gbox.getTiles(data.tileset);
+
+ var obj=gbox.addObject(
+ help.mergeWithModel(
+ data,{
+ zindex:0,
+ fliph:false, flipv:false,
+ id:id,
+ group:gr,
+ cnt:0,
+ tileset:"",
+ frames:{},
+ camera:true,
+ x:data.tilex*mts.tilew,
+ y:data.tiley*mts.tileh,
+ z:0,
+ tilemap:null,
+ defaulttile:0,
+ map:map,
+ colx:(data.fullhit?0:null),
+ coly:(data.fullhit?0:null),
+ colh:(data.fullhit?ts.tileh:null),
+ colw:(data.fullhit?ts.tilew:null),
+ opening:false,
+ doorheight:ts.tileh,
+ opencounter:0,
+ opening:false,
+ closing:false,
+ audiobefore:null,
+ audioafter:null,
+ doOpen:function() {
+ this.opening=true;
+ },
+ whenClosed:toys.NOOP,
+ whenOpened:toys.NOOP,
+ whileMoving:toys.NOOP,
+ hitByBullet:function(by) {
+
+ }
+ }
+ )
+ );
+
+ // Closing animation
+ if (obj.closing) obj.opencounter=obj.doorheight;
+
+ obj.initialize=function() {
+ this.ismoving=false;
+ toys.topview.initialize(this);
+ };
+
+ obj[(data.logicon==null?"first":data.logicon)]=function() {
+ if (this.closing) {
+ if (!this.ismoving) {
+ if (this.audiobefore) gbox.hitAudio(this.audiobefore);
+ this.ismoving=true;
+ }
+ this.whileMoving();
+ this.opencounter--;
+ if (this.opencounter<0) {
+ if (this.audioafter) gbox.hitAudio(this.audioafter);
+ this.ismoving=false;
+ this.opencounter=0;
+ this.closing=false;
+ this.whenClosed();
+ }
+ }
+ if (this.opening) {
+ if (!this.ismoving) {
+ if (this.audiobefore) gbox.hitAudio(this.audiobefore);
+ this.ismoving=true;
+ }
+ this.whileMoving();
+ this.opencounter++;
+ if (this.opencounter>=this.doorheight) {
+ if (this.audioafter) gbox.hitAudio(this.audioafter);
+ this.ismoving=false;
+ if (!this.whenOpened()) gbox.trashObject(this);
+ }
+ }
+ }
+
+ obj[(data.bliton==null?"blit":data.bliton)]=function() {
+ if (!gbox.objectIsTrash(this))
+ gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.cnt,this.frames),dx:this.x,dy:this.y+this.z+this.opencounter,h:this.h-this.opencounter,camera:this.camera,fliph:this.fliph,flipv:this.flipv});
+ }
+
+ gbox.setZindex(obj,obj.y+obj.h);
+
+ return obj;
+ },
+ // Set the object speed making sure that the X and Y coords are multiple of the speed. Useful on maze-based games.
+ setStaticSpeed:function(th,speed) {
+ th.staticspeed=speed;
+ th.x=Math.round(th.x/speed)*speed;
+ th.y=Math.round(th.y/speed)*speed;
+ }
+ },
+
+
+ /**
+ * @namespace shmup The libraries for a 2D top-down Shmup game.
+ */
+ // Shoot'em up specifics
+ shmup:{
+
+ /**
+ *
+ */
+ initialize:function(th,data) {
+ help.mergeWithModel(
+ th,
+ help.mergeWithModel(
+ data,
+ {
+ x:0, y:0,
+ accx:0, accy:0,
+ frames:{},
+ maxacc:5, controlmaxacc:5,
+ responsive:0, // Responsiveness
+ bounds:{x:0,y:0,w:gbox.getScreenW(),h:gbox.getScreenH()}, // Bounds box (ship cannot exit from there)
+ weapon:0, // Weapon
+ hittime:5,
+ camera:false,
+ flipv:false, fliph:false,
+ health:1,
+ tolerance:4
+ }
+ )
+ );
+ toys.shmup.spawn(th);
+ },
+
+ /**
+ *
+ */
+ spawn:function(th,data) {
+ th.xpushing=toys.PUSH_NONE; // user is moving side
+ th.vpushing=toys.PUSH_NONE; // user is moving side
+ th.counter=0; // self counter
+ th.hittimer=0;
+ th.killed=false;
+ help.copyModel(th,data);
+ },
+
+ /**
+ *
+ */
+ getNextX:function(th) { return th.x+help.limit(th.accx,-th.maxacc,th.maxacc); },
+
+ /**
+ *
+ */
+ getNextY:function(th) { return th.y+help.limit(th.accy,-th.maxacc,th.maxacc); },
+
+ /**
+ *
+ */
+ controlKeys:function(th,keys) {
+
+ if (gbox.keyIsPressed(keys.left)) {
+ th.xpushing=toys.PUSH_LEFT;
+ if (th.accx>th.responsive) th.accx=th.responsive;
+ th.accx=help.limit(th.accx-1,-th.controlmaxacc,th.controlmaxacc);
+ } else if (gbox.keyIsPressed(keys.right)) {
+ th.xpushing=toys.PUSH_RIGHT;
+ if (th.accx<-th.responsive) th.accx=-th.responsive;
+ th.accx=help.limit(th.accx+1,-th.controlmaxacc,th.controlmaxacc);
+ } else th.xpushing=toys.PUSH_NONE;
+ if (gbox.keyIsPressed(keys.up)) {
+ th.ypushing=toys.PUSH_UP;
+ if (th.accy>th.responsive) th.accy=th.responsive;
+ th.accy=help.limit(th.accy-1,-th.controlmaxacc,th.controlmaxacc);
+ } else if (gbox.keyIsPressed(keys.down)) {
+ th.ypushing=toys.PUSH_DOWN;
+ if (th.accy<-th.responsive) th.accy=-th.responsive;
+ th.accy=help.limit(th.accy+1,-th.controlmaxacc,th.controlmaxacc);
+ } else th.ypushing=toys.PUSH_NONE;
+ },
+
+ /**
+ *
+ */
+ applyForces:function(th) {
+ th.x=toys.shmup.getNextX(th);
+ th.y=toys.shmup.getNextY(th);
+ },
+
+ /**
+ *
+ */
+ handleAccellerations:function(th) {
+ if (!th.xpushing) th.accx=help.goToZero(th.accx);
+ if (!th.ypushing) th.accy=help.goToZero(th.accy);
+ },
+
+ /**
+ *
+ */
+ keepInBounds:function(th) {
+ if (th.xth.bounds.x+th.bounds.w) {
+ th.x=th.bounds.x+th.bounds.w-th.w;
+ th.accx=0;
+ }
+ if (th.yth.bounds.y+th.bounds.h) {
+ th.y=th.bounds.y+th.bounds.h-th.h;
+ th.accy=0;
+ }
+ },
+
+ /**
+ *
+ */
+ setFrame:function(th) {
+ if (th.hittimer) th.hittimer--;
+ th.frame=help.decideFrame(th.counter,(th.hittimer?th.frames.hit:th.frames.still));
+ },
+
+ /**
+ *
+ */
+ fireBullet:function(gr,id,data) {
+
+ var ts=gbox.getTiles(data.tileset);
+
+ var obj=gbox.addObject(
+ help.mergeWithModel(
+ data,{
+ _bullet:true,
+ fliph:false, flipv:false,
+ id:id,
+ group:gr,
+ cnt:0,
+ tileset:"",
+ frames:{},
+ acc:0,
+ angle:0,
+ camera:false,
+ accx:(data.accx==null?Math.floor(trigo.translateX(0,data.angle,data.acc)):0),
+ accy:(data.accy==null?Math.floor(trigo.translateY(0,data.angle,data.acc)):0),
+ x:data.from.x+data.from.hw-ts.tilehw+(data.gapx?data.gapx:0),
+ y:(data.upper?data.from.y-ts.tilehh+(data.gapy?data.gapy:0):data.from.y+data.from.h-ts.tilehh-(data.gapy?data.gapy:0)),
+ collidegroup:"",
+ spark:toys.NOOP,
+ power:1
+ }
+ )
+ );
+
+ obj[(data.logicon==null?"first":data.logicon)]=function() {
+ this.x+=this.accx;
+ this.y+=this.accy;
+ this.cnt=(this.cnt+1)%10;
+ if (!gbox.objectIsVisible(this)) gbox.trashObject(this);
+ else if (this.collidegroup!=null)
+ for (var i in gbox._objects[this.collidegroup])
+ if ((!gbox._objects[this.collidegroup][i].initialize)&&gbox.collides(this,gbox._objects[this.collidegroup][i],gbox._objects[this.collidegroup][i].tolerance)) {
+ if (gbox._objects[this.collidegroup][i]["hitByBullet"]!=null)
+ if (!gbox._objects[this.collidegroup][i].hitByBullet(this)) {
+ this.spark(this);
+ gbox.trashObject(this);
+ }
+ }
+ }
+
+ obj[(data.bliton==null?"blit":data.bliton)]=function() {
+ gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.cnt,this.frames),dx:this.x,dy:this.y,camera:this.camera,fliph:this.side,flipv:this.flipv});
+ }
+
+ return obj;
+
+ },
+
+ /**
+ *
+ */
+ hitByBullet:function(th,by) {
+ if (by.power) {
+ th.health-=by.power;
+ if (th.health<=0) th.kill(by); else th.hittimer=th.hittime;
+ }
+ },
+
+ /**
+ *
+ */
+ generateEnemy:function(gr,id,data,model) {
+ help.mergeWithModel(data,model);
+ var obj=gbox.addObject(
+ help.mergeWithModel(
+ data,{
+ id:id,
+ group:gr,
+ cnt:0,
+ tileset:"",
+ frames:{},
+ acc:0,
+ angle:0,
+ camera:false,
+ fliph:false,
+ flipv:false,
+ accx:(data.accx==null?Math.floor(trigo.translateX(0,data.angle,data.acc)):0),
+ accy:(data.accy==null?Math.floor(trigo.translateY(0,data.angle,data.acc)):0),
+ x:data.x,
+ y:data.y,
+ // -- spec
+ animationset:"still",
+ defaultanimationset:"still",
+ hitanimationset:"still",
+ hittime:5,
+ script:toys.NOOP,
+ handler:toys.NOOP,
+ scriptline:(data.scriptline==null?-1:data.scriptline-1),
+ newline:true,
+ waitframes:0,
+ doframes:0,
+ mode:0,
+ line:{},
+ dohandler:null,
+ ended:false,
+ health:1,
+ hittimer:0,
+ kill:toys.NOOP,
+ tolerance:0,
+ initialize:null,
+ invulnerable:false,
+ hitAnimation:function(time) {
+ this.hittimer=(time==null?this.hittime:time);
+ this.animationset=this.hitanimationset;
+ },
+ goTo:function(nl) { // Jump to a line
+ this.waitframes=0;
+ this.doframes=0;
+ this.line={};
+ this.scriptline=nl-1;
+ },
+ hitByBullet:function(by) {
+ if (!this.invulnerable&&by.power) {
+ this.health-=by.power;
+ if (this.health<=0) this.kill(this,by); else this.hitAnimation();
+ }
+ }
+ }
+ )
+ );
+
+
+ obj[(data.logicon==null?"first":data.logicon)]=function() {
+ if (this.initialize!=null) {
+ this.initialize(this);
+ this.initialize=null;
+ }
+ if (!this.ended) {
+ if (!this.waitframes&&!this.doframes&&((this.line.waitfor==null)||this.line.waitfor(this))) {
+ this.scriptline++;
+ this.everycnt=-1;
+ if (this.script[this.scriptline]==null)
+ this.ended=true;
+ else {
+ if (this.script[this.scriptline]["goto"]!=null) this.scriptline=this.script[this.scriptline]["goto"];
+ this.line=this.script[this.scriptline];
+ if (this.line.afterframes!=null)
+ this.waitframes=this.line.afterframes;
+ if (this.line.forframes!=null)
+ this.doframes=this.line.forframes;
+ else
+ this.doframes=1;
+ if (this.line.cleardo)
+ this.dohandler=null;
+ else if (this.line.doit!=null) {
+ this.dohandler={
+ actiontimes:0,
+ timer:(this.line.doit.every=="keep"?this.dohandler.every:this.line.doit.every),
+ every:(this.line.doit.every=="keep"?this.dohandler.every:this.line.doit.every),
+ once:(this.line.doit.once=="keep"?this.dohandler.once:this.line.doit.once),
+ action:(this.line.doit.action=="keep"?this.dohandler.action:this.line.doit.action),
+ render:(this.line.doit.render=="keep"?this.dohandler.render:this.line.doit.render)
+ }
+ }
+
+ }
+ }
+ if (!this.waitframes&&this.doframes&&!this.ended) {
+ this.doframes--;
+ if (this.line.setinvulnerable!=null) this.invulnerable=this.line.setinvulnerable;
+ if (this.line.setx!=null) this.x=this.line.setx;
+ if (this.line.sety!=null) this.y=this.line.sety;
+ if (this.line.addx!=null) this.x+=this.line.addx;
+ if (this.line.addy!=null) this.y+=this.line.addy;
+ if (this.line.setaccx!=null) this.accx=this.line.setaccx;
+ if (this.line.setaccy!=null) this.accy=this.line.setaccy;
+ if (this.line.setacc!=null) {
+ this.acc=this.line.setacc;
+ this.accx=Math.floor(trigo.translateX(0,this.angle,this.acc));
+ this.accy=Math.floor(trigo.translateY(0,this.angle,this.acc));
+ }
+ if (this.line.addaccx!=null) this.accx+=this.line.addaccx;
+ if (this.line.addaccy!=null) this.accy+=this.line.addaccy;
+ if (this.line.addacc!=null) {
+ this.acc+=this.line.addacc;
+ this.accx=Math.floor(trigo.translateX(0,this.angle,this.acc));
+ this.accy=Math.floor(trigo.translateY(0,this.angle,this.acc));
+ }
+
+ if (this.line.setangle!=null) {
+ this.angle=this.line.setangle;
+ this.accx=Math.floor(trigo.translateX(0,this.angle,this.acc));
+ this.accy=Math.floor(trigo.translateY(0,this.angle,this.acc));
+ }
+ if (this.line.addangle!=null) {
+ this.angle+=this.line.addangle;
+ this.accx=Math.floor(trigo.translateX(0,this.angle,this.acc));
+ this.accy=Math.floor(trigo.translateY(0,this.angle,this.acc));
+ }
+ if (this.line.everyframe) this.waitframes=this.line.everyframe;
+
+ }
+ if (this.waitframes>0) this.waitframes--;
+ }
+ if (this.dohandler&&(this.dohandler.action!=null)) {
+ if (this.dohandler.timer==this.dohandler.every) {
+ this.dohandler.action(this,this.dohandler.actiontimes);
+ this.dohandler.timer=0;
+ this.dohandler.actiontimes++;
+ } else if (!this.dohandler.once) this.dohandler.timer++;
+ }
+ if (this.handler!=null) this.handler(this);
+
+ if (this.hittimer) {
+ this.hittimer--;
+ if (!this.hittimer) this.animationset=this.defaultanimationset;
+ }
+
+ this.x+=this.accx;
+ this.y+=this.accy;
+ this.cnt=(this.cnt+1)%10;
+
+ }
+
+ obj[(data.bliton==null?"blit":data.bliton)]=function() {
+ gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.cnt,this.frames[this.animationset]),dx:this.x,dy:this.y,camera:this.camera,fliph:this.side,flipv:this.flipv});
+ if (this.dohandler&&(this.dohandler.render!=null)) this.dohandler.render(this);
+ }
+
+ return obj;
+
+ },
+
+ /**
+ *
+ */
+ generateScroller:function(gr,id,data) {
+ var obj=gbox.addObject(
+ help.mergeWithModel(
+ help.cloneObject(data),{
+ id:id, group:gr,
+ y:0, x:0,
+ stage:{},
+ speed:0,
+ stop:null, // Remember to set the last stop ever! or the last loop!
+ block:-1,
+ bly:0,
+ lblock:-1,
+ lbly:0,
+ lget:0,
+ tbly:0,
+ trb:0,
+ maxwidth:0,
+ loopstart:null, loopend:null, looprounds:0,
+ panspeed:1, panstimer:0, destspeed:0,
+
+ setLoop:function(st,en) {
+ this.loopstart=st;
+ this.loopend=en;
+ this.lget=1;
+ this.looprounds=1;
+ },
+
+ quitLoop:function() {
+ this.setLoop(null,null);
+ this.looprounds=0;
+ },
+
+ setSpeed:function(s) {
+ this.speed=s;
+ this.destspeed=s;
+ },
+
+ panToSpeed:function(s,pans) {
+ this.destspeed=s;
+ this.panspeed=pans;
+ },
+
+ quitStop:function() {
+ this.stop=null;
+ },
+
+ setStop:function(s) {
+ this.stop=s;
+ },
+
+ setX:function(x) {
+ if (x<0) this.x=0; else
+ if (x+gbox.getScreenW()>this.maxwidth) this.x=this.maxwidth-gbox.getScreenW();
+ else this.x=x;
+ }
+
+ }
+ )
+ );
+
+ obj[(data.logicon==null?"first":data.logicon)]=function() {
+ if ((this.stop==null)||(this.ythis.destspeed) this.speed--;
+ this.panstimer=this.panspeed;
+ }
+ }
+ this.y+=this.speed;
+ if ((this.stop!=null)&&(this.y>=this.stop)) this.y=this.stop;
+ if ((this.loopend!=null)&&(this.y>this.loopend)) {
+ this.y=this.loopstart+(this.y-this.loopend);
+ this.looprounds++;
+ if (this.lget==1) {
+ this.block=0;
+ this.bly=0;
+ this.lget=2;
+ } else {
+ this.block=this.lblock;
+ this.bly=this.lbly;
+ }
+
+ }
+ }
+
+ // Cerca il blocco da mostrare
+ this.trb=this.block;
+ this.tbly=this.bly;
+ do {
+ this.trb++;
+ this.tbly+=gbox.getImage(this.stage[this.trb].image).height;
+ } while (this.tblygbox.getScreenH()) done=true;
+ gbox.blitAll(gbox.getBufferContext(),gbox.getImage(this.stage[this.trb].image),{dx:-this.x,dy:gbox.getScreenH()-dy});
+ this.trb++;
+ dy+=gbox.getImage(this.stage[this.trb].image).height;
+ } while (!done);
+ }
+
+ return obj;
+ }
+ },
+
+ /**
+ * @namespace platformer The libraries for generating a 2D platformer game.
+ */
+ platformer:{
+
+ /**
+ *
+ */
+ initialize:function(th,data) {
+ help.mergeWithModel(
+ th,
+ help.mergeWithModel(
+ data,
+ {
+ maxaccx:5, maxaccy:10,
+ jumpsize:6, jumpaccy:6,
+ accx:0, accy:0,
+ x:0, y:0,
+ frames:{},
+ camera:true,
+ flipv:false,
+ side:false
+ }
+ )
+ );
+ toys.platformer.spawn(th);
+ },
+
+ /**
+ *
+ */
+ spawn:function(th,data) {
+ th.curjsize=0; // current jump size
+ th.counter=0; // self counter
+ th.touchedfloor=false; // touched floor
+ th.touchedceil=false;
+ th.touchedleftwall=false;
+ th.touchedrightwall=false;
+ th.pushing=toys.PUSH_NONE; // user is moving side
+ th.killed=false;
+ help.copyModel(th,data);
+ },
+
+ /**
+ *
+ */
+ getNextX:function(th) { return th.x+th.accx; },
+
+ /**
+ *
+ */
+ getNextY:function(th) { return th.y+help.limit(th.accy,-th.maxaccy,th.maxaccy); },
+
+ /**
+ *
+ */
+ applyGravity:function(th) {
+ th.x=toys.platformer.getNextX(th);
+ th.y=toys.platformer.getNextY(th);
+ },
+
+ /**
+ *
+ */
+ horizontalKeys:function(th,keys) {
+ if (gbox.keyIsPressed(keys.left)) {
+ th.pushing=toys.PUSH_LEFT;
+ th.accx=help.limit(th.accx-1,-th.maxaccx,th.maxaccx);
+ } else if (gbox.keyIsPressed(keys.right)) {
+ th.pushing=toys.PUSH_RIGHT;
+ th.accx=help.limit(th.accx+1,-th.maxaccx,th.maxaccx);
+ } else th.pushing=toys.PUSH_NONE;
+ },
+
+ /**
+ *
+ */
+ verticalTileCollision:function(th,map,tilemap) {
+ var bottom=help.getTileInMap(th.x+(th.w/2),th.y+th.h,map,0,tilemap);
+ var top=help.getTileInMap(th.x+(th.w/2),th.y,map,0,tilemap);
+ th.touchedfloor=false;
+ th.touchedceil=false;
+
+ if (map.tileIsSolidCeil(th,top)) {
+ th.accy=0;
+ th.y=help.yPixelToTile(map,th.y,1);
+ th.touchedceil=true;
+ }
+ if (map.tileIsSolidFloor(th,bottom)) {
+ th.accy=0;
+ th.y=help.yPixelToTile(map,th.y+th.h)-th.h;
+ th.touchedfloor=true;
+ }
+ },
+
+ /**
+ *
+ */
+ horizontalTileCollision:function(th,map,tilemap,precision) {
+ var left=0;
+ var right=0;
+ var t=0;
+
+ th.touchedleftwall=false;
+ th.touchedrightwall=false;
+
+ while (t0)&&map.tileIsSolidFloor(th,right)) {
+ th.accx=0;
+ th.x=help.xPixelToTile(map,th.x+th.w)-th.w;
+ th.touchedrightwall=true;
+ }
+ t+=gbox.getTiles(map.tileset).tileh/(precision?precision:1);
+ }
+ },
+
+ /**
+ * Checks if the passed object is touching the floor and can therefore jump at present.
+ * @param th This is the object being checked for jump ability at the time of calling.
+ */
+ canJump:function(th) {
+ return th.touchedfloor;
+ },
+
+ /**
+ *
+ */
+ jumpKeys:function(th,key) {
+ if ((toys.platformer.canJump(th)||(key.doublejump&&(th.accy>=0)))&&gbox.keyIsHit(key.jump)&&(th.curjsize==0)) {
+ if (key.audiojump) gbox.hitAudio(key.audiojump);
+ th.accy=-th.jumpaccy;
+ th.curjsize=th.jumpsize;
+ return true;
+ } else if (th.curjsize&&gbox.keyIsHold(key.jump)) { // Jump modulation
+ th.accy--;
+ th.curjsize--;
+ } else
+ th.curjsize=0;
+ return false;
+ },
+
+ /**
+ *
+ */
+ bounce:function(th,data) {
+ th.curjsize=0;
+ th.accy=-data.jumpsize;
+ },
+
+ /**
+ *
+ */
+ handleAccellerations:function(th) {
+ // Gravity
+ if (!th.touchedfloor) th.accy++;
+ // Attrito
+ if (th.pushing==toys.PUSH_NONE) if (th.accx) th.accx=help.goToZero(th.accx);
+ },
+
+ /**
+ *
+ */
+ setSide:function(th) {
+ if (th.accx) th.side=th.accx>0;
+ },
+
+ /**
+ *
+ */
+ setFrame:function(th) {
+ if (th.touchedfloor)
+ if (th.pushing!=toys.PUSH_NONE)
+ th.frame=help.decideFrame(th.counter,th.frames.walking);
+ else
+ th.frame=help.decideFrame(th.counter,th.frames.still);
+ else if (th.accy>0)
+ th.frame=help.decideFrame(th.counter,th.frames.falling);
+ else
+ th.frame=help.decideFrame(th.counter,th.frames.jumping);
+ },
+
+ /**
+ *
+ */
+ auto:{
+ // Moves on a platform. It tries to do not fall down, if specified.
+ // Args: (object,{moveWhileFalling:,speed:})
+ // Outs: the frame
+ goomba:function(th,data) {
+ if (data.moveWhileFalling||th.touchedfloor) {
+ if (th.side) {
+ th.pushing=toys.PUSH_LEFT;
+ th.accx=-data.speed;
+ } else {
+ th.pushing=toys.PUSH_RIGHT;
+ th.accx=data.speed;
+ }
+ } else th.pushing=toys.PUSH_NONE;
+ },
+ dontFall:function(th,map,tilemap) {
+ if (th.accx&&th.touchedfloor) {
+ var til;
+ if (th.accx>0) til=help.getTileInMap(toys.platformer.getNextX(th)+th.w-1+th.accx,th.y+th.h,map,0,tilemap);
+ else til=help.getTileInMap(toys.platformer.getNextX(th),th.y+th.h,map,0,tilemap);
+ if (!map.tileIsSolidFloor(th,til)) {
+ th.side=!th.side;
+ th.accx=0;
+ }
+ }
+ },
+ horizontalBounce:function(th) {
+ if (th.touchedleftwall||th.touchedrightwall) th.side=!th.side;
+ }
+ }
+ },
+
+ // State-based toys
+ // CONSTANTS
+ TOY_BUSY:0,
+ TOY_DONE:1,
+ TOY_IDLE:2,
+
+ // PRIVATE
+
+ // Generical toys method
+
+ /**
+ *
+ */
+ resetToy:function(th,id) { if (th.toys) delete th.toys[id] },
+
+ /**
+ *
+ */
+ getToyValue:function(th,id,v,def) { return ((th.toys==null)||(th.toys[id]==null)?def:th.toys[id][v]) },
+
+ /**
+ *
+ */
+ getToyStatus:function(th,id) { return ((th.toys==null)||(th.toys[id]==null)?toys.TOY_BUSY:th.toys[id].__status) },
+
+ /**
+ *
+ */
+ _toydone:function(th,id) {
+ if (th.toys[id].__statusw) w=opt.items[i].length;
+ gbox.createCanvas("menu-"+id,{w:w*fd.tilew,h:opt.items.length*fd.tileh});
+ for (var i=0;i0)) {
+ if (opt.audiooption) gbox.hitAudio(opt.audiooption);
+ th.toys[id].selected--;
+ } else
+ if (gbox.keyIsHit(opt.keys.down)&&(th.toys[id].selected0)
+ if (th.toys[id].ok<10) {
+ th.toys[id].ok++;
+ toys._toybusy(th,id);
+ } else return toys._toydone(th,id); // selected > 0
+ else return toys._toydone(th,id); // selected == -1
+ } else return toys._toybusy(th,id);
+ },
+
+ /**
+ *
+ */
+ // Returns a full customizable object for optimized huds
+ hud:function(id) {
+ gbox.createCanvas(id);
+ return {
+ w:{},
+ surfaceid:id,
+
+ /**
+ *
+ */
+ updateWidget:function(i){
+ if (!this.w[i].__hidden) {
+ if (this.w[i].widget=="label") {
+ if (this.w[i].prepad!=null) this.w[i].text=help.prepad(this.w[i].value,this.w[i].prepad,this.w[i].padwith); else
+ if (this.w[i].postpad!=null) this.w[i].text=help.postpad(this.w[i].value,this.w[i].postpad,this.w[i].padwith); else
+ this.w[i].text=this.w[i].value+"";
+ gbox.blitText(gbox.getCanvasContext(this.surfaceid),this.w[i]);
+ }
+ if (this.w[i].widget=="symbols") {
+ var ts=gbox.getTiles(this.w[i].tileset);
+ gbox.blitClear(gbox.getCanvasContext(this.surfaceid),{x:this.w[i].dx,y:this.w[i].dy,w:((this.w[i].maxshown-1)*this.w[i].gapx)+ts.tilew,h:((this.w[i].maxshown-1)*this.w[i].gapy)+ts.tileh});
+ var cnt=this.w[i].value;
+ for (var x=0;x0) {
+ gbox.blitTile(gbox.getCanvasContext(this.surfaceid),{tileset:this.w[i].tileset,tile:this.w[i].tiles[(cnt>this.w[i].tiles.length?this.w[i].tiles.length-1:cnt-1)],dx:this.w[i].dx+(x*this.w[i].gapx),dy:this.w[i].dy+(x*this.w[i].gapy),fliph:this.w[i].fliph,flipv:this.w[i].flipv});
+ } else
+ if (this.w[i].emptytile!=null)
+ gbox.blitTile(gbox.getCanvasContext(this.surfaceid),{tileset:this.w[i].tileset,tile:this.w[i].emptytile,dx:this.w[i].dx+(x*this.w[i].gapx),dy:this.w[i].dy+(x*this.w[i].gapy),fliph:this.w[i].fliph,flipv:this.w[i].flipv});
+ cnt-=this.w[i].tiles.length;
+ }
+
+ }
+ if (this.w[i].widget=="stack") {
+ var ts=gbox.getTiles(this.w[i].tileset);
+ var bw=((this.w[i].maxshown-1)*this.w[i].gapx)+ts.tilew;
+ gbox.blitClear(gbox.getCanvasContext(this.surfaceid),{x:this.w[i].dx-(this.w[i].rightalign?bw:0),y:this.w[i].dy,w:bw,h:((this.w[i].maxshown-1)*this.w[i].gapy)+ts.tileh});
+ for (var x=0;xthis.w[w].maxvalue)) v=this.w[w].maxvalue;
+ if ((this.w[w].minvalue!=null)&&(v1) th.toys[id].fade=1;
+ data.alpha=th.toys[id].fade;
+ gbox.blitFade(tox,data);
+ if (data.audiofade) gbox.setAudioVolume(data.audiofade,th.toys[id].stv*(1-data.alpha));
+ if (data.audiochannelfade)
+ if (data.alpha==1)
+ gbox.stopChannel(data.audiochannelfade);
+ else
+ gbox.setChannelVolume(data.audiochannelfade,th.toys[id].chv*(1-data.alpha));
+ return toys._toyfrombool(th,id,th.toys[id].fade==1)
+ },
+
+ /**
+ *
+ */
+ fadein:function(th,id,tox,data) {
+ if (toys._maketoy(th,id)||data.resetfade) {
+ th.toys[id].fade=1;
+ if (data.audiofade) th.toys[id].stv=gbox.getAudioVolume(data.audiofade);
+ if (data.audiochannelfade) th.toys[id].chv=gbox.getChannelDefaultVolume(data.audiochannelfade);
+ }
+ th.toys[id].fade-=data.fadespeed;
+ if (th.toys[id].fade<0) th.toys[id].fade=0;
+ if (th.toys[id].fade) {
+ data.alpha=th.toys[id].fade;
+ if (data.audiofade) gbox.setAudioVolume(data.audiofade,th.toys[id].stv*(1-data.alpha));
+ if (data.audiochannelfade) gbox.setChannelVolume(data.audiochannelfade,th.toys[id].chv*(1-data.alpha));
+ gbox.blitFade(tox,data);
+ }
+ return toys._toyfrombool(th,id,th.toys[id].fade==0);
+ }
+ },
+
+ /**
+ *
+ */
+ text:{
+
+ /**
+ *
+ */
+ blink:function(th,id,tox,data) {
+ if (toys._maketoy(th,id)) {
+ th.toys[id].texttimer=0;
+ th.toys[id].visible=false;
+ th.toys[id].times=0;
+ }
+ if (th.toys[id].texttimer>=data.blinkspeed) {
+ th.toys[id].texttimer=0;
+ th.toys[id].visible=!th.toys[id].visible;
+ if (data.times) th.toys[id].times++;
+ } else th.toys[id].texttimer++;
+ if (th.toys[id].visible)
+ gbox.blitText(tox,data);
+ return toys._toyfrombool(th,id,(data.times?data.timesth.toys[id].lh) th.toys[id].gapx=th.toys[id].lh;
+ gbox.blit(gbox.getBufferContext(),gbox.getImage(data.image),{dh:th.toys[id].cnt,dw:th.toys[id].lw,dx:data.x,dy:data.y+th.toys[id].lh-th.toys[id].cnt,alpha:data.alpha});
+ if (data.reflex) gbox.blit(gbox.getBufferContext(),gbox.getImage(data.image),{dh:th.toys[id].cnt,dw:th.toys[id].lw,dx:data.x,dy:data.y+th.toys[id].lh,alpha:data.reflex,flipv:true});
+ if (th.toys[id].cnt>=th.toys[id].lh)
+ if (data.audioreach) gbox.hitAudio(data.audioreach);
+ return toys._toybusy(th,id);
+ } else {
+ gbox.blitAll(gbox.getBufferContext(),gbox.getImage(data.image),{dx:data.x,dy:data.y});
+ if (data.reflex) gbox.blitAll(gbox.getBufferContext(),gbox.getImage(data.image),{dx:data.x,dy:data.y+th.toys[id].lh,alpha:data.reflex,flipv:true});
+
+ return toys._toydone(th,id);
+ }
+ },
+
+ /**
+ *
+ */
+ bounce:function(th,id,data) {
+ if (toys._maketoy(th,id)) {
+ th.toys[id].accy=data.accy;
+ th.toys[id].y=data.y;
+ th.toys[id].h=gbox.getImage(data.image).height;
+ th.toys[id].done=false;
+ }
+ if (!th.toys[id].done) {
+ if (th.toys[id].y+th.toys[id].h>=data.floory) {
+ if (data.audiobounce) gbox.hitAudio(data.audiobounce);
+ th.toys[id].y=data.floory-th.toys[id].h;
+ th.toys[id].accy=-Math.ceil(th.toys[id].accy/(data.heavy?data.heavy:2));
+ th.toys[id].done=(th.toys[id].accy==0);
+ } else th.toys[id].accy--;
+ th.toys[id].y-=th.toys[id].accy;
+ }
+ gbox.blitAll(gbox.getBufferContext(),gbox.getImage(data.image),{dx:data.x,dy:th.toys[id].y});
+
+ return toys._toyfrombool(th,id,th.toys[id].done);
+ }
+ },
+
+ /**
+ *
+ */
+ dialogue: {
+
+ /**
+ *
+ */
+ render:function(th,id,data){
+ if (toys._maketoy(th,id)) {
+ th.toys[id].newscene=true;
+ th.toys[id].sceneid=-1;
+ th.toys[id].ended=false;
+ th.toys[id].timer=0;
+ th.toys[id].counter=0;
+ th.toys[id].anim=0;
+ gbox.createCanvas("dialogue-"+id);
+ }
+ if (!data.hideonend||(data.hideonend&&!th.toys[id].ended)) {
+ if (th.toys[id].newscene&&!th.toys[id].ended) {
+ th.toys[id].anim=0;
+ th.toys[id].timer=0;
+ th.toys[id].newscene=false;
+ th.toys[id].sceneid++;
+ th.toys[id].ended=(th.toys[id].sceneid==data.scenes.length);
+ if (!th.toys[id].ended) {
+ th.toys[id].letter=0;
+ th.toys[id].wait=false;
+ th.toys[id].scene=data.scenes[th.toys[id].sceneid];
+ th.toys[id].fd=gbox.getFont((th.toys[id].scene.font?th.toys[id].scene.font:data.font));
+ th.toys[id].sceneH=(th.toys[id].scene.dh?th.toys[id].scene.dh:gbox.getScreenH());
+ th.toys[id].sceneW=(th.toys[id].scene.dw?th.toys[id].scene.dw:gbox.getScreenW());
+ th.toys[id].sceneX=(th.toys[id].scene.dx?th.toys[id].scene.dx:0);
+ th.toys[id].sceneY=(th.toys[id].scene.dy?th.toys[id].scene.dy:0);
+ gbox.blitClear(gbox.getCanvasContext("dialogue-"+id));
+ if (th.toys[id].scene.slide) {
+ gbox.blitAll(gbox.getCanvasContext("dialogue-"+id),gbox.getImage(th.toys[id].scene.slide.image),{dx:th.toys[id].scene.slide.x,dy:th.toys[id].scene.slide.y});
+ }
+ if (th.toys[id].scene.scroller) {
+ gbox.createCanvas("scroller-"+id,{w:th.toys[id].sceneW,h:(th.toys[id].scene.scroller.length)*(th.toys[id].fd.tileh+th.toys[id].scene.spacing)});
+ for (var i=0;ith.toys[id].scene.talk[row].length) {
+ tmp-=th.toys[id].scene.talk[row].length;
+ row++;
+ if (row==th.toys[id].scene.talk.length) {
+ row=-1;
+ break;
+ }
+ }
+ if (row>=0) {
+ gbox.blitText(gbox.getCanvasContext("dialogue-"+id),{
+ font:data.font,
+ dx:data.who[th.toys[id].scene.who].x,
+ dy:(data.who[th.toys[id].scene.who].y)+(row*th.toys[id].fd.tileh),
+ text:th.toys[id].scene.talk[row].substr(0,tmp)
+ });
+ } else
+ th.toys[id].wait=true;
+ }
+
+ } else if (th.toys[id].scene.scroller) { // SCROLLER (i.e. credits)
+
+ if (th.toys[id].counter==th.toys[id].scene.speed) {
+ th.toys[id].letter++;
+ th.toys[id].counter=0;
+ if (th.toys[id].letter==(gbox.getCanvas("scroller-"+id).height+th.toys[id].scene.push))
+ th.toys[id].wait=true;
+ }
+
+ } else if (th.toys[id].scene.bonus) { // BONUS (classic bonus award screen)
+ for (var row=0;row<=th.toys[id].letter;row++) {
+ if (th.toys[id].scene.bonus[row].text)
+ gbox.blitText(gbox.getCanvasContext("bonus-"+id),{
+ font:data.font,
+ dx:0,
+ dy:(row*(th.toys[id].fd.tileh+th.toys[id].scene.spacing)),
+ text:th.toys[id].scene.bonus[row].text
+ });
+ else if (th.toys[id].scene.bonus[row].mul) {
+ // Mask is %VAL%e%MUL%=%TOT%
+ th.toys[id].scene.bonus[row].tmptext=th.toys[id].scene.bonus[row].mask.replace(/%VAL%/,th.toys[id].timer).replace(/%MUL%/,th.toys[id].scene.bonus[row].mul).replace(/%TOT%/,(th.toys[id].timer*th.toys[id].scene.bonus[row].mul));
+ gbox.blitText(gbox.getCanvasContext("bonus-"+id),{
+ clear:true,
+ font:data.font,
+ dx:0,
+ dy:(row*(th.toys[id].fd.tileh+th.toys[id].scene.spacing)),
+ text:th.toys[id].scene.bonus[row].tmptext
+ });
+ }
+ }
+
+ if (!th.toys[id].wait) {
+ var next=false;
+ if (th.toys[id].scene.bonus[th.toys[id].letter].mul&&!th.toys[id].scene.bonus[th.toys[id].letter].text) {
+ if (th.toys[id].counter>=th.toys[id].scene.bonus[th.toys[id].letter].speed) {
+ th.toys[id].counter=0;
+ th.toys[id].timer++;
+ if (th.toys[id].timer>th.toys[id].scene.bonus[th.toys[id].letter].mulvalue) {
+ th.toys[id].scene.bonus[th.toys[id].letter].text=th.toys[id].scene.bonus[th.toys[id].letter].tmptext;
+ next=true;
+ } else {
+ if (th.toys[id].scene.bonus[th.toys[id].letter].callback)
+ th.toys[id].scene.bonus[th.toys[id].letter].callback(th.toys[id].scene.bonus[th.toys[id].letter],th.toys[id].scene.bonus[th.toys[id].letter].arg);
+ }
+ }
+
+ } else if (th.toys[id].counter>=th.toys[id].scene.speed) next=true;
+ if (next) {
+ if (th.toys[id].letter==th.toys[id].scene.bonus.length-1)
+ th.toys[id].wait=true;
+ else {
+ th.toys[id].letter++;
+ if (th.toys[id].scene.bonus[th.toys[id].letter].mul) {
+ th.toys[id].scene.bonus[th.toys[id].letter].text=null;
+ th.toys[id].scene.bonus[th.toys[id].letter].tmptext=null;
+ th.toys[id].timer=0;
+ }
+ th.toys[id].counter=0;
+ }
+ }
+ }
+ }
+
+ }
+
+ }
+
+ // RENDERING
+
+
+ if (th.toys[id].scene.talk) { // DIALOGUES
+ if (data.who[th.toys[id].scene.who].box)
+ gbox.blitRect(gbox.getBufferContext(),data.who[th.toys[id].scene.who].box);
+ if (data.who[th.toys[id].scene.who].tileset) {
+ th.toys[id].anim=(th.toys[id].anim+1)%20;
+ gbox.blitTile(gbox.getBufferContext(),{tileset:data.who[th.toys[id].scene.who].tileset,tile:help.decideFrame(th.toys[id].anim,data.who[th.toys[id].scene.who].frames),dx:data.who[th.toys[id].scene.who].portraitx,dy:data.who[th.toys[id].scene.who].portraity,camera:false,fliph:data.who[th.toys[id].scene.who].fliph,flipv:data.who[th.toys[id].scene.who].flipv});
+ }
+ gbox.blitAll(gbox.getBufferContext(),gbox.getCanvas("dialogue-"+id),{dx:0,dy:0});
+ } else if (th.toys[id].scene.scroller) // SCROLLER (i.e. credits)
+ gbox.blit(gbox.getBufferContext(),gbox.getCanvas("scroller-"+id),{dx:th.toys[id].sceneX,dy:th.toys[id].sceneY+(th.toys[id].letter=0) {
+ this.x+=this.accx;
+ this.y+=this.accy;
+ if (this.gravity) this.accy++;
+ if ((this.timer==this.toptimer)||(this.trashoffscreen&&(!gbox.objectIsVisible(this)))) gbox.trashObject(this);
+ }
+ }
+
+ obj[(data.bliton==null?"blit":data.bliton)]=function() {
+ if ((this.timer>=0)&&(!this.blinkspeed||(Math.floor(this.timer/this.blinkspeed)%2)))
+ gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.timer,this.frames),dx:this.x,dy:this.y,camera:this.camera,fliph:this.fliph,flipv:this.flipv,alpha:this.alpha});
+ }
+
+ return obj;
+ },
+
+ /**
+ *
+ */
+ popupText:function(th,group,id,data) {
+ data.text+="";
+ var fd=gbox.getFont(data.font);
+
+ var obj=gbox.addObject(
+ help.mergeWithModel(
+ data,{
+ id:id,
+ group:group,
+ x:Math.floor(th.x+th.hw-(fd.tilehw*data.text.length)),
+ y:th.y-fd.tilehh,
+ vaccy:-data.jump,
+ font:"small",
+ keep:0,
+ text:data.text+"",
+ cnt:0,
+ camera:th.camera
+ }
+ )
+ );
+
+ obj.initialize=function() {
+ var fd=gbox.getFont(this.font);
+ gbox.createCanvas("poptext-"+this.id,{w:this.text.length*fd.tilew,h:fd.tileh});
+ gbox.blitText(gbox.getCanvasContext("poptext-"+this.id),{font:this.font,text:this.text,dx:0,dy:0});
+ }
+
+ obj.onpurge=function() {
+ gbox.deleteCanvas("poptext-"+this.id);
+ }
+
+ obj[(data.logicon==null?"first":data.logicon)]=function() {
+ if (gbox.objectIsVisible(this)) {
+ if (this.vaccy)
+ this.vaccy++;
+ else
+ this.cnt++;
+ this.y+=this.vaccy;
+ if (this.cnt>=this.keep) gbox.trashObject(this);
+ } else gbox.trashObject(this);
+ }
+
+ obj[(data.bliton==null?"blit":data.bliton)]=function() {
+ gbox.blitAll(gbox.getBufferContext(),gbox.getCanvas("poptext-"+this.id),{dx:this.x,dy:this.y,camera:this.camera});
+ }
+
+ return obj;
+ },
+
+ /**
+ *
+ */
+ bounceDie:function(th,group,id,data){
+ var obj=gbox.addObject(
+ help.mergeWithModel(
+ data,{
+ id:id,
+ group:group,
+ tileset:th.tileset,
+ frame:th.frame,
+ side:th.side,
+ frames:th.frames.die,
+ x:th.x,
+ y:th.y,
+ vaccy:-data.jump,
+ accx:0,
+ flipv:data.flipv,
+ cnt:0,
+ blinkspeed:0,
+ camera:th.camera
+ }
+ )
+ );
+
+ obj[(data.logicon==null?"first":data.logicon)]=function() {
+ if (gbox.objectIsVisible(this)) {
+ this.vaccy++;
+ this.y+=this.vaccy;
+ this.x+=this.accx;
+ this.cnt++;
+ } else gbox.trashObject(this);
+ }
+
+ obj[(data.bliton==null?"blit":data.bliton)]=function() {
+ if (!this.blinkspeed||(Math.floor(this.cnt/this.blinkspeed)%2))
+ gbox.blitTile(gbox.getBufferContext(),{tileset:this.tileset,tile:help.decideFrame(this.cnt,this.frames),dx:this.x,dy:this.y,camera:this.camera,fliph:this.side,flipv:this.flipv});
+ }
+
+ return obj;
+ }
+ },
+
+ /**
+ *
+ */
+ audio:{
+
+ /**
+ *
+ */
+ fadeOut:function(th,group,id,data){
+ var obj=gbox.addObject(
+ help.mergeWithModel(
+ data,{
+ id:id,
+ group:group,
+ fadespeed:-0.02*(data.fadein?-1:1),
+ stoponmute:true,
+ audio:null,
+ channel:null,
+ destination:null
+ }
+ )
+ );
+
+ obj[(data.logicon==null?"first":data.logicon)]=function() {
+ if (this.destination==null)
+ if (this.audio)
+ if (this.fadespeed>0) this.destination=1; else this.destination=0;
+ else
+ if (this.fadespeed>0) this.destination=gbox.getChannelDefaultVolume(this.channel); else this.destination=0;
+ if (this.fadespeed>0) gbox.playAudio(this.audio);
+ }
+
+ obj[(data.bliton==null?"blit":data.bliton)]=function() {
+ if (this.audio) gbox.changeAudioVolume(this.audio,this.fadespeed);
+ if (this.channel) gbox.changeChannelVolume(this.channel,this.fadespeed);
+ if (
+ (this.audio&&(
+ ((this.fadespeed<0)&&(gbox.getAudioVolume(this.audio)<=this.destination))||
+ ((this.fadespeed>0)&&(gbox.getAudioVolume(this.audio)>=this.destination))
+ ))||
+ (this.channel&&(
+ ((this.fadespeed<0)&&(gbox.getChannelVolume(this.channel)<=this.destination))||
+ ((this.fadespeed>0)&&(gbox.getChannelVolume(this.channel)>=this.destination))
+ ))
+ ) {
+ if (this.channel&&this.stoponmute&&(this.fadespeed<0)) gbox.stopChannel(this.channel);
+ if (this.audio&&this.stoponmute&&(this.fadespeed<0)) gbox.stopAudio(this.audio);
+ gbox.trashObject(this);
+ }
+ }
+ }
+
+ }
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/public/akihabara/trigo.js b/public/akihabara/trigo.js
new file mode 100644
index 0000000..de75b23
--- /dev/null
+++ b/public/akihabara/trigo.js
@@ -0,0 +1,76 @@
+// ---
+// Copyright (c) 2010 Francesco Cottone, http://www.kesiev.com/
+// ---
+
+/**
+ * @namespace Trigo module provides some math stuff for moving objects in a
+ * direction or following a round path.
+ */
+var trigo={
+ ANGLE_RIGHT:0,
+ ANGLE_DOWN:Math.PI*0.5,
+ ANGLE_LEFT:Math.PI,
+ ANGLE_UP:Math.PI*1.5555555,
+
+ /**
+ * Adds two angles together (radians).
+ * @param {Float} a Base angle.
+ * @param {Float} add The angle you're adding to the base angle.
+ * @returns The resultant angle, always between 0 and 2*pi.
+ */
+ addAngle:function(a,add) {
+ a=(a+add)%(Math.PI*2);
+ if (a<0) return (Math.PI*2)+a; else return a;
+ },
+ /**
+ * Gets the distance between two points.
+ * @param {Object} p1 This is an object containing x and y params for the first point.
+ * @param {Object} p2 This is an object containing x and y params for the second point.
+ * @returns The distance between p1 and p2.
+ */
+ getDistance:function(p1,p2) {
+ return Math.sqrt(Math.pow(p2.x-p1.x,2)+Math.pow(p2.y-p1.y,2))
+ },
+
+ /**
+ * Calculates the angle between two points.
+ * @param {Object} p1 This is an object containing x and y params for the first point.
+ * @param {Object} p2 This is an object containing x and y params for the second point.
+ * @param {Float} transl (Optional) Adds an angle (in radians) to the result. Defaults to 0.
+ * @returns The angle between points p1 and p2, plus transl.
+ */
+ getAngle:function(p1,p2,transl) {
+ return this.addAngle(Math.atan2(p2.y-p1.y,p2.x-p1.x),(transl?transl:0));
+ },
+
+ /**
+ * Translates a point by a vector defined by angle and distance. This does not return a value but rather modifies the x and y values of p1.
+ * @param {Object} p1 This is an object containing x and y params for the point.
+ * @param {Float} a The angle of translation (rad).
+ * @param {Float} d The distance of translation.
+ */
+ translate:function(p1,a,d) {
+ p1.x=p1.x+Math.cos(a)*d;
+ p1.y=p1.y+Math.sin(a)*d;
+ },
+
+ /**
+ * Translates an x component of a coordinate by a vector defined by angle and distance. This returns its component translation.
+ * @param {Float} x1 This is an x coordinate.
+ * @param {Float} a The angle of translation (rad).
+ * @param {Float} d The distance of translation.
+ */
+ translateX:function(x1,a,d) {
+ return x1+Math.cos(a)*d
+ },
+
+ /**
+ * Translates a y component of a coordinate by a vector defined by angle and distance. This returns its component translation.
+ * @param {Float} y1 This is a y coordinate.
+ * @param {Float} a The angle of translation (rad).
+ * @param {Float} d The distance of translation.
+ */
+ translateY:function(y1,a,d) {
+ return y1+Math.sin(a)*d;
+ }
+}
diff --git a/public/binary.js b/public/binary.js
new file mode 100644
index 0000000..ed3ba69
--- /dev/null
+++ b/public/binary.js
@@ -0,0 +1,188 @@
+/**
+ * Module dependencies.
+ */
+var Buffer = require('buffer').Buffer; // TODO just use global Buffer
+var bson = require('./bson');
+
+/**
+ * A class representation of the BSON Binary type.
+ *
+ * Sub types
+ * - **BSON.BSON_BINARY_SUBTYPE_DEFAULT**, default BSON type.
+ * - **BSON.BSON_BINARY_SUBTYPE_FUNCTION**, BSON function type.
+ * - **BSON.BSON_BINARY_SUBTYPE_BYTE_ARRAY**, BSON byte array type.
+ * - **BSON.BSON_BINARY_SUBTYPE_UUID**, BSON uuid type.
+ * - **BSON.BSON_BINARY_SUBTYPE_MD5**, BSON md5 type.
+ * - **BSON.BSON_BINARY_SUBTYPE_USER_DEFINED**, BSON user defined type.
+ *
+ * @class Represents the Binary BSON type.
+ * @param {Buffer} buffer a buffer object containing the binary data.
+ * @param {Number} [subType] the option binary type.
+ * @return {Grid}
+ */
+function Binary(buffer, subType) {
+ this._bsontype = 'Binary';
+
+ if(buffer instanceof Number) {
+ this.sub_type = buffer;
+ this.position = 0;
+ } else {
+ this.sub_type = subType == null ? bson.BSON.BSON_BINARY_SUBTYPE_DEFAULT : subType;
+ this.position = 0;
+ }
+
+ if(buffer != null && !(buffer instanceof Number)) {
+ this.buffer = typeof buffer == 'string' ? new Buffer(buffer) : buffer;
+ this.position = buffer.length;
+ } else {
+ this.buffer = new Buffer(Binary.BUFFER_SIZE);
+ this.position = 0;
+ }
+};
+
+/**
+ * Updates this binary with byte_value.
+ *
+ * @param {Character} byte_value a single byte we wish to write.
+ * @api public
+ */
+Binary.prototype.put = function put (byte_value) {
+ if(this.buffer.length > this.position) {
+ this.buffer[this.position++] = byte_value.charCodeAt(0);
+ } else {
+ // Create additional overflow buffer
+ var buffer = new Buffer(Binary.BUFFER_SIZE + this.buffer.length);
+ // Combine the two buffers together
+ this.buffer.copy(buffer, 0, 0, this.buffer.length);
+ this.buffer = buffer;
+ this.buffer[this.position++] = byte_value.charCodeAt(0);
+ }
+};
+
+/**
+ * Writes.
+ *
+ * @param {Buffer|String} string a string or buffer to be written to the Binary BSON object.
+ * @param {Number} offset specify the binary of where to write the content.
+ * @api public
+ */
+Binary.prototype.write = function write(string, offset) {
+ offset = offset ? offset : this.position;
+
+ // If the buffer is to small let's extend the buffer
+ if (this.buffer.length < offset + string.length) {
+ var buffer = new Buffer(this.buffer.length + string.length);
+ this.buffer.copy(buffer, 0, 0, this.buffer.length);
+ // Assign the new buffer
+ this.buffer = buffer;
+ }
+
+ if(Buffer.isBuffer(string)) {
+ string.copy(this.buffer, offset, 0, string.length);
+ } else {
+ this.buffer.write(string, 'binary', offset);
+ }
+
+ this.position = offset + string.length;
+};
+
+/**
+ * Reads **length** bytes starting at **position**.
+ *
+ * @param {Number} position read from the given position in the Binary.
+ * @param {Number} length the number of bytes to read.
+ * @return {Buffer}
+ * @api public
+ */
+Binary.prototype.read = function read(position, length) {
+ length = length && length > 0
+ ? length
+ : this.position;
+
+ // Return the buffer
+ return this.buffer.slice(position, position + length);
+};
+
+/**
+ * Returns the value of this binary as a string.
+ *
+ * @return {String}
+ * @api public
+ */
+Binary.prototype.value = function value(asRaw) {
+ asRaw = asRaw == null ? false : asRaw;
+ return asRaw ? this.buffer.slice(0, this.position) : this.buffer.toString('binary', 0, this.position);
+};
+
+/**
+ * Length.
+ *
+ * @return {Number} the length of the binary.
+ * @api public
+ */
+Binary.prototype.length = function length() {
+ return this.position;
+};
+
+/**
+ * @ignore
+ * @api private
+ */
+Binary.prototype.toJSON = function() {
+ return this.buffer != null ? this.buffer.toString('base64') : '';
+}
+
+/**
+ * @ignore
+ * @api private
+ */
+Binary.prototype.toString = function(format) {
+ return this.buffer != null ? this.buffer.slice(0, this.position).toString(format) : '';
+}
+
+Binary.BUFFER_SIZE = 256;
+
+/**
+ * Default BSON type
+ *
+ * @classconstant SUBTYPE_DEFAULT
+ **/
+Binary.SUBTYPE_DEFAULT = 0;
+/**
+ * Function BSON type
+ *
+ * @classconstant SUBTYPE_DEFAULT
+ **/
+Binary.SUBTYPE_FUNCTION = 1;
+/**
+ * Byte Array BSON type
+ *
+ * @classconstant SUBTYPE_DEFAULT
+ **/
+Binary.SUBTYPE_BYTE_ARRAY = 2;
+/**
+ * UUID BSON type
+ *
+ * @classconstant SUBTYPE_DEFAULT
+ **/
+Binary.SUBTYPE_UUID = 3;
+/**
+ * MD5 BSON type
+ *
+ * @classconstant SUBTYPE_DEFAULT
+ **/
+Binary.SUBTYPE_MD5 = 4;
+/**
+ * User BSON type
+ *
+ * @classconstant SUBTYPE_DEFAULT
+ **/
+Binary.SUBTYPE_USER_DEFINED = 128;
+
+/**
+ * Expose.
+ */
+if(typeof window === 'undefined') {
+ exports.Binary = Binary;
+}
+
diff --git a/public/binary_parser.js b/public/binary_parser.js
new file mode 100644
index 0000000..f752fee
--- /dev/null
+++ b/public/binary_parser.js
@@ -0,0 +1,386 @@
+/**
+ * Binary Parser.
+ * Jonas Raoni Soares Silva
+ * http://jsfromhell.com/classes/binary-parser [v1.0]
+ */
+var chr = String.fromCharCode;
+
+var maxBits = [];
+for (var i = 0; i < 64; i++) {
+ maxBits[i] = Math.pow(2, i);
+}
+
+function BinaryParser (bigEndian, allowExceptions) {
+ this.bigEndian = bigEndian;
+ this.allowExceptions = allowExceptions;
+};
+
+BinaryParser.warn = function warn (msg) {
+ if (this.allowExceptions) {
+ throw new Error(msg);
+ }
+
+ return 1;
+};
+
+BinaryParser.decodeFloat = function decodeFloat (data, precisionBits, exponentBits) {
+ var b = new this.Buffer(this.bigEndian, data);
+
+ b.checkBuffer(precisionBits + exponentBits + 1);
+
+ var bias = maxBits[exponentBits - 1] - 1
+ , signal = b.readBits(precisionBits + exponentBits, 1)
+ , exponent = b.readBits(precisionBits, exponentBits)
+ , significand = 0
+ , divisor = 2
+ , curByte = b.buffer.length + (-precisionBits >> 3) - 1;
+
+ do {
+ for (var byteValue = b.buffer[ ++curByte ], startBit = precisionBits % 8 || 8, mask = 1 << startBit; mask >>= 1; ( byteValue & mask ) && ( significand += 1 / divisor ), divisor *= 2 );
+ } while (precisionBits -= startBit);
+
+ return exponent == ( bias << 1 ) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity : ( 1 + signal * -2 ) * ( exponent || significand ? !exponent ? Math.pow( 2, -bias + 1 ) * significand : Math.pow( 2, exponent - bias ) * ( 1 + significand ) : 0 );
+};
+
+BinaryParser.decodeInt = function decodeInt (data, bits, signed, forceBigEndian) {
+ var b = new this.Buffer(this.bigEndian || forceBigEndian, data)
+ , x = b.readBits(0, bits)
+ , max = maxBits[bits]; //max = Math.pow( 2, bits );
+
+ return signed && x >= max / 2
+ ? x - max
+ : x;
+};
+
+BinaryParser.encodeFloat = function encodeFloat (data, precisionBits, exponentBits) {
+ var bias = maxBits[exponentBits - 1] - 1
+ , minExp = -bias + 1
+ , maxExp = bias
+ , minUnnormExp = minExp - precisionBits
+ , n = parseFloat(data)
+ , status = isNaN(n) || n == -Infinity || n == +Infinity ? n : 0
+ , exp = 0
+ , len = 2 * bias + 1 + precisionBits + 3
+ , bin = new Array(len)
+ , signal = (n = status !== 0 ? 0 : n) < 0
+ , intPart = Math.floor(n = Math.abs(n))
+ , floatPart = n - intPart
+ , lastBit
+ , rounded
+ , result
+ , i
+ , j;
+
+ for (i = len; i; bin[--i] = 0);
+
+ for (i = bias + 2; intPart && i; bin[--i] = intPart % 2, intPart = Math.floor(intPart / 2));
+
+ for (i = bias + 1; floatPart > 0 && i; (bin[++i] = ((floatPart *= 2) >= 1) - 0 ) && --floatPart);
+
+ for (i = -1; ++i < len && !bin[i];);
+
+ if (bin[(lastBit = precisionBits - 1 + (i = (exp = bias + 1 - i) >= minExp && exp <= maxExp ? i + 1 : bias + 1 - (exp = minExp - 1))) + 1]) {
+ if (!(rounded = bin[lastBit])) {
+ for (j = lastBit + 2; !rounded && j < len; rounded = bin[j++]);
+ }
+
+ for (j = lastBit + 1; rounded && --j >= 0; (bin[j] = !bin[j] - 0) && (rounded = 0));
+ }
+
+ for (i = i - 2 < 0 ? -1 : i - 3; ++i < len && !bin[i];);
+
+ if ((exp = bias + 1 - i) >= minExp && exp <= maxExp) {
+ ++i;
+ } else if (exp < minExp) {
+ exp != bias + 1 - len && exp < minUnnormExp && this.warn("encodeFloat::float underflow");
+ i = bias + 1 - (exp = minExp - 1);
+ }
+
+ if (intPart || status !== 0) {
+ this.warn(intPart ? "encodeFloat::float overflow" : "encodeFloat::" + status);
+ exp = maxExp + 1;
+ i = bias + 2;
+
+ if (status == -Infinity) {
+ signal = 1;
+ } else if (isNaN(status)) {
+ bin[i] = 1;
+ }
+ }
+
+ for (n = Math.abs(exp + bias), j = exponentBits + 1, result = ""; --j; result = (n % 2) + result, n = n >>= 1);
+
+ for (n = 0, j = 0, i = (result = (signal ? "1" : "0") + result + bin.slice(i, i + precisionBits).join("")).length, r = []; i; j = (j + 1) % 8) {
+ n += (1 << j) * result.charAt(--i);
+ if (j == 7) {
+ r[r.length] = String.fromCharCode(n);
+ n = 0;
+ }
+ }
+
+ r[r.length] = n
+ ? String.fromCharCode(n)
+ : "";
+
+ return (this.bigEndian ? r.reverse() : r).join("");
+};
+
+BinaryParser.encodeInt = function encodeInt (data, bits, signed, forceBigEndian) {
+ var max = maxBits[bits];
+
+ if (data >= max || data < -(max / 2)) {
+ this.warn("encodeInt::overflow");
+ data = 0;
+ }
+
+ if (data < 0) {
+ data += max;
+ }
+
+ for (var r = []; data; r[r.length] = String.fromCharCode(data % 256), data = Math.floor(data / 256));
+
+ for (bits = -(-bits >> 3) - r.length; bits--; r[r.length] = "\0");
+
+ return ((this.bigEndian || forceBigEndian) ? r.reverse() : r).join("");
+};
+
+BinaryParser.toSmall = function( data ){ return this.decodeInt( data, 8, true ); };
+BinaryParser.fromSmall = function( data ){ return this.encodeInt( data, 8, true ); };
+BinaryParser.toByte = function( data ){ return this.decodeInt( data, 8, false ); };
+BinaryParser.fromByte = function( data ){ return this.encodeInt( data, 8, false ); };
+BinaryParser.toShort = function( data ){ return this.decodeInt( data, 16, true ); };
+BinaryParser.fromShort = function( data ){ return this.encodeInt( data, 16, true ); };
+BinaryParser.toWord = function( data ){ return this.decodeInt( data, 16, false ); };
+BinaryParser.fromWord = function( data ){ return this.encodeInt( data, 16, false ); };
+BinaryParser.toInt = function( data ){ return this.decodeInt( data, 32, true ); };
+BinaryParser.fromInt = function( data ){ return this.encodeInt( data, 32, true ); };
+BinaryParser.toLong = function( data ){ return this.decodeInt( data, 64, true ); };
+BinaryParser.fromLong = function( data ){ return this.encodeInt( data, 64, true ); };
+BinaryParser.toDWord = function( data ){ return this.decodeInt( data, 32, false ); };
+BinaryParser.fromDWord = function( data ){ return this.encodeInt( data, 32, false ); };
+BinaryParser.toQWord = function( data ){ return this.decodeInt( data, 64, true ); };
+BinaryParser.fromQWord = function( data ){ return this.encodeInt( data, 64, true ); };
+BinaryParser.toFloat = function( data ){ return this.decodeFloat( data, 23, 8 ); };
+BinaryParser.fromFloat = function( data ){ return this.encodeFloat( data, 23, 8 ); };
+BinaryParser.toDouble = function( data ){ return this.decodeFloat( data, 52, 11 ); };
+BinaryParser.fromDouble = function( data ){ return this.encodeFloat( data, 52, 11 ); };
+
+// Factor out the encode so it can be shared by add_header and push_int32
+BinaryParser.encode_int32 = function encode_int32 (number, asArray) {
+ var a, b, c, d, unsigned;
+ unsigned = (number < 0) ? (number + 0x100000000) : number;
+ a = Math.floor(unsigned / 0xffffff);
+ unsigned &= 0xffffff;
+ b = Math.floor(unsigned / 0xffff);
+ unsigned &= 0xffff;
+ c = Math.floor(unsigned / 0xff);
+ unsigned &= 0xff;
+ d = Math.floor(unsigned);
+ return asArray ? [chr(a), chr(b), chr(c), chr(d)] : chr(a) + chr(b) + chr(c) + chr(d);
+};
+
+BinaryParser.encode_int64 = function encode_int64 (number) {
+ var a, b, c, d, e, f, g, h, unsigned;
+ unsigned = (number < 0) ? (number + 0x10000000000000000) : number;
+ a = Math.floor(unsigned / 0xffffffffffffff);
+ unsigned &= 0xffffffffffffff;
+ b = Math.floor(unsigned / 0xffffffffffff);
+ unsigned &= 0xffffffffffff;
+ c = Math.floor(unsigned / 0xffffffffff);
+ unsigned &= 0xffffffffff;
+ d = Math.floor(unsigned / 0xffffffff);
+ unsigned &= 0xffffffff;
+ e = Math.floor(unsigned / 0xffffff);
+ unsigned &= 0xffffff;
+ f = Math.floor(unsigned / 0xffff);
+ unsigned &= 0xffff;
+ g = Math.floor(unsigned / 0xff);
+ unsigned &= 0xff;
+ h = Math.floor(unsigned);
+ return chr(a) + chr(b) + chr(c) + chr(d) + chr(e) + chr(f) + chr(g) + chr(h);
+};
+
+/**
+ * UTF8 methods
+ */
+
+// Take a raw binary string and return a utf8 string
+BinaryParser.decode_utf8 = function decode_utf8 (binaryStr) {
+ var len = binaryStr.length
+ , decoded = ''
+ , i = 0
+ , c = 0
+ , c1 = 0
+ , c2 = 0
+ , c3;
+
+ while (i < len) {
+ c = binaryStr.charCodeAt(i);
+ if (c < 128) {
+ decoded += String.fromCharCode(c);
+ i++;
+ } else if ((c > 191) && (c < 224)) {
+ c2 = binaryStr.charCodeAt(i+1);
+ decoded += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
+ i += 2;
+ } else {
+ c2 = binaryStr.charCodeAt(i+1);
+ c3 = binaryStr.charCodeAt(i+2);
+ decoded += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
+ i += 3;
+ }
+ }
+
+ return decoded;
+};
+
+// Encode a cstring
+BinaryParser.encode_cstring = function encode_cstring (s) {
+ return unescape(encodeURIComponent(s)) + BinaryParser.fromByte(0);
+};
+
+// Take a utf8 string and return a binary string
+BinaryParser.encode_utf8 = function encode_utf8 (s) {
+ var a = ""
+ , c;
+
+ for (var n = 0, len = s.length; n < len; n++) {
+ c = s.charCodeAt(n);
+
+ if (c < 128) {
+ a += String.fromCharCode(c);
+ } else if ((c > 127) && (c < 2048)) {
+ a += String.fromCharCode((c>>6) | 192) ;
+ a += String.fromCharCode((c&63) | 128);
+ } else {
+ a += String.fromCharCode((c>>12) | 224);
+ a += String.fromCharCode(((c>>6) & 63) | 128);
+ a += String.fromCharCode((c&63) | 128);
+ }
+ }
+
+ return a;
+};
+
+BinaryParser.hprint = function hprint (s) {
+ var number;
+
+ for (var i = 0, len = s.length; i < len; i++) {
+ if (s.charCodeAt(i) < 32) {
+ number = s.charCodeAt(i) <= 15
+ ? "0" + s.charCodeAt(i).toString(16)
+ : s.charCodeAt(i).toString(16);
+ process.stdout.write(number + " ")
+ } else {
+ number = s.charCodeAt(i) <= 15
+ ? "0" + s.charCodeAt(i).toString(16)
+ : s.charCodeAt(i).toString(16);
+ process.stdout.write(number + " ")
+ }
+ }
+
+ process.stdout.write("\n\n");
+};
+
+BinaryParser.ilprint = function hprint (s) {
+ var number;
+
+ for (var i = 0, len = s.length; i < len; i++) {
+ if (s.charCodeAt(i) < 32) {
+ number = s.charCodeAt(i) <= 15
+ ? "0" + s.charCodeAt(i).toString(10)
+ : s.charCodeAt(i).toString(10);
+
+ require('util').debug(number+' : ');
+ } else {
+ number = s.charCodeAt(i) <= 15
+ ? "0" + s.charCodeAt(i).toString(10)
+ : s.charCodeAt(i).toString(10);
+ require('util').debug(number+' : '+ s.charAt(i));
+ }
+ }
+};
+
+BinaryParser.hlprint = function hprint (s) {
+ var number;
+
+ for (var i = 0, len = s.length; i < len; i++) {
+ if (s.charCodeAt(i) < 32) {
+ number = s.charCodeAt(i) <= 15
+ ? "0" + s.charCodeAt(i).toString(16)
+ : s.charCodeAt(i).toString(16);
+ require('util').debug(number+' : ');
+ } else {
+ number = s.charCodeAt(i) <= 15
+ ? "0" + s.charCodeAt(i).toString(16)
+ : s.charCodeAt(i).toString(16);
+ require('util').debug(number+' : '+ s.charAt(i));
+ }
+ }
+};
+
+/**
+ * BinaryParser buffer constructor.
+ */
+
+function BinaryParserBuffer (bigEndian, buffer) {
+ this.bigEndian = bigEndian || 0;
+ this.buffer = [];
+ this.setBuffer(buffer);
+};
+
+BinaryParserBuffer.prototype.setBuffer = function setBuffer (data) {
+ var l, i, b;
+
+ if (data) {
+ i = l = data.length;
+ b = this.buffer = new Array(l);
+ for (; i; b[l - i] = data.charCodeAt(--i));
+ this.bigEndian && b.reverse();
+ }
+};
+
+BinaryParserBuffer.prototype.hasNeededBits = function hasNeededBits (neededBits) {
+ return this.buffer.length >= -(-neededBits >> 3);
+};
+
+BinaryParserBuffer.prototype.checkBuffer = function checkBuffer (neededBits) {
+ if (!this.hasNeededBits(neededBits)) {
+ throw new Error("checkBuffer::missing bytes");
+ }
+};
+
+BinaryParserBuffer.prototype.readBits = function readBits (start, length) {
+ //shl fix: Henri Torgemane ~1996 (compressed by Jonas Raoni)
+
+ function shl (a, b) {
+ for (; b--; a = ((a %= 0x7fffffff + 1) & 0x40000000) == 0x40000000 ? a * 2 : (a - 0x40000000) * 2 + 0x7fffffff + 1);
+ return a;
+ }
+
+ if (start < 0 || length <= 0) {
+ return 0;
+ }
+
+ this.checkBuffer(start + length);
+
+ var offsetLeft
+ , offsetRight = start % 8
+ , curByte = this.buffer.length - ( start >> 3 ) - 1
+ , lastByte = this.buffer.length + ( -( start + length ) >> 3 )
+ , diff = curByte - lastByte
+ , sum = ((this.buffer[ curByte ] >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1)) + (diff && (offsetLeft = (start + length) % 8) ? (this.buffer[lastByte++] & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight : 0);
+
+ for(; diff; sum += shl(this.buffer[lastByte++], (diff-- << 3) - offsetRight));
+
+ return sum;
+};
+
+/**
+ * Expose.
+ */
+BinaryParser.Buffer = BinaryParserBuffer;
+
+if(typeof window === 'undefined') {
+ exports.BinaryParser = BinaryParser;
+}
diff --git a/public/bson.js b/public/bson.js
new file mode 100644
index 0000000..694809d
--- /dev/null
+++ b/public/bson.js
@@ -0,0 +1,1360 @@
+if(typeof window === 'undefined') {
+ var Long = require('./long').Long
+ , Double = require('./double').Double
+ , Timestamp = require('./timestamp').Timestamp
+ , ObjectID = require('./objectid').ObjectID
+ , Symbol = require('./symbol').Symbol
+ , Code = require('./code').Code
+ , MinKey = require('./min_key').MinKey
+ , MaxKey = require('./max_key').MaxKey
+ , DBRef = require('./db_ref').DBRef
+ , Binary = require('./binary').Binary
+ , ieee754 = require('./float_parser');
+} else {
+ // Let's create our own Buffer class
+ Buffer = Array;
+ // Add missing methods
+ Buffer.byteLength = function(string, encoding) {
+ return string.length;
+ }
+
+ Buffer.prototype.write = function(data, index, encoding) {
+ index = index == null || isNaN(index) ? 0 : index;
+
+ for(var i = 0; i < data.length; i++) {
+ this[index + i] = data[i].charCodeAt(0);
+ }
+
+ // return the number of bytes written
+ return data.length;
+ }
+
+ Buffer.prototype.asArrayBuffer = function() {
+ return new Uint8Array(this).buffer;
+ }
+}
+
+/**
+ * Create a new BSON instance
+ *
+ * @class Represents the BSON Parser
+ * @return {BSON} instance of BSON Parser.
+ */
+function BSON () {};
+
+/**
+ * @ignore
+ * @api private
+ */
+// BSON MAX VALUES
+BSON.BSON_INT32_MAX = 0x7FFFFFFF;
+BSON.BSON_INT32_MIN = -0x80000000;
+
+BSON.BSON_INT64_MAX = Math.pow(2, 63) - 1;
+BSON.BSON_INT64_MIN = -Math.pow(2, 63);
+
+// JS MAX PRECISE VALUES
+BSON.JS_INT_MAX = 0x20000000000000; // Any integer up to 2^53 can be precisely represented by a double.
+BSON.JS_INT_MIN = -0x20000000000000; // Any integer down to -2^53 can be precisely represented by a double.
+
+// Internal long versions
+var JS_INT_MAX_LONG = Long.fromNumber(0x20000000000000); // Any integer up to 2^53 can be precisely represented by a double.
+var JS_INT_MIN_LONG = Long.fromNumber(-0x20000000000000); // Any integer down to -2^53 can be precisely represented by a double.
+
+/**
+ * Number BSON Type
+ *
+ * @classconstant BSON_DATA_NUMBER
+ **/
+BSON.BSON_DATA_NUMBER = 1;
+/**
+ * String BSON Type
+ *
+ * @classconstant BSON_DATA_STRING
+ **/
+BSON.BSON_DATA_STRING = 2;
+/**
+ * Object BSON Type
+ *
+ * @classconstant BSON_DATA_OBJECT
+ **/
+BSON.BSON_DATA_OBJECT = 3;
+/**
+ * Array BSON Type
+ *
+ * @classconstant BSON_DATA_ARRAY
+ **/
+BSON.BSON_DATA_ARRAY = 4;
+/**
+ * Binary BSON Type
+ *
+ * @classconstant BSON_DATA_BINARY
+ **/
+BSON.BSON_DATA_BINARY = 5;
+/**
+ * ObjectID BSON Type
+ *
+ * @classconstant BSON_DATA_OID
+ **/
+BSON.BSON_DATA_OID = 7;
+/**
+ * Boolean BSON Type
+ *
+ * @classconstant BSON_DATA_BOOLEAN
+ **/
+BSON.BSON_DATA_BOOLEAN = 8;
+/**
+ * Date BSON Type
+ *
+ * @classconstant BSON_DATA_DATE
+ **/
+BSON.BSON_DATA_DATE = 9;
+/**
+ * null BSON Type
+ *
+ * @classconstant BSON_DATA_NULL
+ **/
+BSON.BSON_DATA_NULL = 10;
+/**
+ * RegExp BSON Type
+ *
+ * @classconstant BSON_DATA_REGEXP
+ **/
+BSON.BSON_DATA_REGEXP = 11;
+/**
+ * Code BSON Type
+ *
+ * @classconstant BSON_DATA_CODE
+ **/
+BSON.BSON_DATA_CODE = 13;
+/**
+ * Symbol BSON Type
+ *
+ * @classconstant BSON_DATA_SYMBOL
+ **/
+BSON.BSON_DATA_SYMBOL = 14;
+/**
+ * Code with Scope BSON Type
+ *
+ * @classconstant BSON_DATA_CODE_W_SCOPE
+ **/
+BSON.BSON_DATA_CODE_W_SCOPE = 15;
+/**
+ * 32 bit Integer BSON Type
+ *
+ * @classconstant BSON_DATA_INT
+ **/
+BSON.BSON_DATA_INT = 16;
+/**
+ * Timestamp BSON Type
+ *
+ * @classconstant BSON_DATA_TIMESTAMP
+ **/
+BSON.BSON_DATA_TIMESTAMP = 17;
+/**
+ * Long BSON Type
+ *
+ * @classconstant BSON_DATA_LONG
+ **/
+BSON.BSON_DATA_LONG = 18;
+/**
+ * MinKey BSON Type
+ *
+ * @classconstant BSON_DATA_MIN_KEY
+ **/
+BSON.BSON_DATA_MIN_KEY = 0xff;
+/**
+ * MaxKey BSON Type
+ *
+ * @classconstant BSON_DATA_MAX_KEY
+ **/
+BSON.BSON_DATA_MAX_KEY = 0x7f;
+
+/**
+ * Binary Default Type
+ *
+ * @classconstant BSON_BINARY_SUBTYPE_DEFAULT
+ **/
+BSON.BSON_BINARY_SUBTYPE_DEFAULT = 0;
+/**
+ * Binary Function Type
+ *
+ * @classconstant BSON_BINARY_SUBTYPE_FUNCTION
+ **/
+BSON.BSON_BINARY_SUBTYPE_FUNCTION = 1;
+/**
+ * Binary Byte Array Type
+ *
+ * @classconstant BSON_BINARY_SUBTYPE_BYTE_ARRAY
+ **/
+BSON.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2;
+/**
+ * Binary UUID Type
+ *
+ * @classconstant BSON_BINARY_SUBTYPE_UUID
+ **/
+BSON.BSON_BINARY_SUBTYPE_UUID = 3;
+/**
+ * Binary MD5 Type
+ *
+ * @classconstant BSON_BINARY_SUBTYPE_MD5
+ **/
+BSON.BSON_BINARY_SUBTYPE_MD5 = 4;
+/**
+ * Binary User Defined Type
+ *
+ * @classconstant BSON_BINARY_SUBTYPE_USER_DEFINED
+ **/
+BSON.BSON_BINARY_SUBTYPE_USER_DEFINED = 128;
+
+/**
+ * Calculate the bson size for a passed in Javascript object.
+ *
+ * @param {Object} object the Javascript object to calculate the BSON byte size for.
+ * @param {Boolean} [serializeFunctions] serialize all functions in the object **(default:false)**.
+ * @return {Number} returns the number of bytes the BSON object will take up.
+ * @api public
+ */
+BSON.calculateObjectSize = function calculateObjectSize(object, serializeFunctions) {
+ var totalLength = (4 + 1);
+
+ if(Array.isArray(object)) {
+ for(var i = 0; i < object.length; i++) {
+ totalLength += calculateElement(i.toString(), object[i], serializeFunctions)
+ }
+ } else {
+ for(var key in object) {
+ totalLength += calculateElement(key, object[key], serializeFunctions)
+ }
+ }
+
+ return totalLength;
+}
+
+/**
+ * @ignore
+ * @api private
+ */
+function calculateElement(name, value, serializeFunctions) {
+ switch(typeof value) {
+ case 'string':
+ return 1 + Buffer.byteLength(name, 'utf8') + 1 + 4 + Buffer.byteLength(value, 'utf8') + 1;
+ case 'number':
+ if(Math.floor(value) === value && value >= BSON.JS_INT_MIN && value <= BSON.JS_INT_MAX) {
+ if(value >= BSON.BSON_INT32_MIN && value <= BSON.BSON_INT32_MAX) { // 32 bit
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + (4 + 1);
+ } else {
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + (8 + 1);
+ }
+ } else { // 64 bit
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + (8 + 1);
+ }
+ case 'undefined':
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + (1);
+ case 'boolean':
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + (1 + 1);
+ case 'object':
+ if(value == null || value instanceof MinKey || value instanceof MaxKey || value['_bsontype'] == 'MinKey' || value['_bsontype'] == 'MaxKey') {
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + (1);
+ } else if(value instanceof ObjectID || value['_bsontype'] == 'ObjectID') {
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + (12 + 1);
+ } else if(value instanceof Date) {
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + (8 + 1);
+ } else if(Buffer.isBuffer(value)) {
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + (1 + 4 + 1) + value.length;
+ } else if(value instanceof Long || value instanceof Double || value instanceof Timestamp
+ || value['_bsontype'] == 'Long' || value['_bsontype'] == 'Double' || value['_bsontype'] == 'Timestamp') {
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + (8 + 1);
+ } else if(value instanceof Code || value['_bsontype'] == 'Code') {
+ // Calculate size depending on the availability of a scope
+ if(value.scope != null && Object.keys(value.scope).length > 0) {
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + 1 + 4 + 4 + Buffer.byteLength(value.code.toString(), 'utf8') + 1 + BSON.calculateObjectSize(value.scope);
+ } else {
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + 1 + 4 + Buffer.byteLength(value.code.toString(), 'utf8') + 1;
+ }
+ } else if(value instanceof Binary || value['_bsontype'] == 'Binary') {
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + (value.position + 1 + 4 + 1);
+ } else if(value instanceof Symbol || value['_bsontype'] == 'Symbol') {
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + (Buffer.byteLength(value.value, 'utf8') + 4 + 1 + 1);
+ } else if(value instanceof DBRef || value['_bsontype'] == 'DBRef') {
+ // Set up correct object for serialization
+ var ordered_values = {
+ '$ref': value.namespace
+ , '$id' : value.oid
+ };
+
+ // Add db reference if it exists
+ if(null != value.db) {
+ ordered_values['$db'] = value.db;
+ }
+
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + 1 + BSON.calculateObjectSize(ordered_values, serializeFunctions);
+ } else if(value instanceof RegExp || toString.call(value) === '[object RegExp]') {
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + 1 + Buffer.byteLength(value.source, 'utf8') + 1
+ + (value.global ? 1 : 0) + (value.ignoreCase ? 1 : 0) + (value.multiline ? 1 : 0) + 1
+ } else {
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + BSON.calculateObjectSize(value, serializeFunctions) + 1;
+ }
+ case 'function':
+ // WTF for 0.4.X where typeof /someregexp/ === 'function'
+ if(value instanceof RegExp || toString.call(value) === '[object RegExp]') {
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + 1 + Buffer.byteLength(value.source, 'utf8') + 1
+ + (value.global ? 1 : 0) + (value.ignoreCase ? 1 : 0) + (value.multiline ? 1 : 0) + 1
+ } else {
+ if(serializeFunctions && value.scope != null && Object.keys(value.scope).length > 0) {
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + 1 + 4 + 4 + Buffer.byteLength(value.toString(), 'utf8') + 1 + BSON.calculateObjectSize(value.scope);
+ } else if(serializeFunctions) {
+ return (name != null ? (Buffer.byteLength(name) + 1) : 0) + 1 + 4 + Buffer.byteLength(value.toString(), 'utf8') + 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Serialize a Javascript object using a predefined Buffer and index into the buffer, useful when pre-allocating the space for serialization.
+ *
+ * @param {Object} object the Javascript object to serialize.
+ * @param {Boolean} checkKeys the serializer will check if keys are valid.
+ * @param {Buffer} buffer the Buffer you pre-allocated to store the serialized BSON object.
+ * @param {Number} index the index in the buffer where we wish to start serializing into.
+ * @param {Boolean} serializeFunctions serialize the javascript functions **(default:false)**.
+ * @return {Number} returns the new write index in the Buffer.
+ * @api public
+ */
+BSON.serializeWithBufferAndIndex = function serializeWithBufferAndIndex(object, checkKeys, buffer, index, serializeFunctions) {
+ // Default setting false
+ serializeFunctions = serializeFunctions == null ? false : serializeFunctions;
+ // Write end information (length of the object)
+ var size = buffer.length;
+ // Write the size of the object
+ buffer[index++] = size & 0xff;
+ buffer[index++] = (size >> 8) & 0xff;
+ buffer[index++] = (size >> 16) & 0xff;
+ buffer[index++] = (size >> 24) & 0xff;
+ return serializeObject(object, checkKeys, buffer, index, serializeFunctions) - 1;
+}
+
+/**
+ * @ignore
+ * @api private
+ */
+var serializeObject = function(object, checkKeys, buffer, index, serializeFunctions) {
+ // Process the object
+ if(Array.isArray(object)) {
+ for(var i = 0; i < object.length; i++) {
+ index = packElement(i.toString(), object[i], checkKeys, buffer, index, serializeFunctions);
+ }
+ } else {
+ for(var key in object) {
+ // Check the key and throw error if it's illegal
+ if(checkKeys == true && (key != '$db' && key != '$ref' && key != '$id')) {
+ BSON.checkKey(key);
+ }
+
+ // Pack the element
+ index = packElement(key, object[key], checkKeys, buffer, index, serializeFunctions);
+ }
+ }
+
+ // Write zero
+ buffer[index++] = 0;
+ return index;
+}
+
+/**
+ * @ignore
+ * @api private
+ */
+var packElement = function(name, value, checkKeys, buffer, index, serializeFunctions) {
+ // console.log("packElement: " + name + " :: " + value)
+ var startIndex = index;
+
+ switch(typeof value) {
+ case 'string':
+ // Encode String type
+ buffer[index++] = BSON.BSON_DATA_STRING;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+
+ // Calculate size
+ var size = Buffer.byteLength(value) + 1;
+ // Write the size of the string to buffer
+ buffer[index + 3] = (size >> 24) & 0xff;
+ buffer[index + 2] = (size >> 16) & 0xff;
+ buffer[index + 1] = (size >> 8) & 0xff;
+ buffer[index] = size & 0xff;
+ // Ajust the index
+ index = index + 4;
+ // Write the string
+ buffer.write(value, index, 'utf8');
+ // Update index
+ index = index + size - 1;
+ // Write zero
+ buffer[index++] = 0;
+ // Return index
+ return index;
+ case 'number':
+ // We have an integer value
+ if(Math.floor(value) === value && value >= BSON.JS_INT_MIN && value <= BSON.JS_INT_MAX) {
+ // If the value fits in 32 bits encode as int, if it fits in a double
+ // encode it as a double, otherwise long
+ if(value >= BSON.BSON_INT32_MIN && value <= BSON.BSON_INT32_MAX) {
+ // Set int type 32 bits or less
+ buffer[index++] = BSON.BSON_DATA_INT;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ // Write the int value
+ buffer[index++] = value & 0xff;
+ buffer[index++] = (value >> 8) & 0xff;
+ buffer[index++] = (value >> 16) & 0xff;
+ buffer[index++] = (value >> 24) & 0xff;
+ } else if(value >= BSON.JS_INT_MIN && value <= BSON.JS_INT_MAX) {
+ // Encode as double
+ buffer[index++] = BSON.BSON_DATA_NUMBER;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ // Write float
+ ieee754.writeIEEE754(buffer, value, index, 'little', 52, 8);
+ // Ajust index
+ index = index + 8;
+ } else {
+ // Set long type
+ buffer[index++] = BSON.BSON_DATA_LONG;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ var longVal = Long.fromNumber(value);
+ var lowBits = longVal.getLowBits();
+ var highBits = longVal.getHighBits();
+ // Encode low bits
+ buffer[index++] = lowBits & 0xff;
+ buffer[index++] = (lowBits >> 8) & 0xff;
+ buffer[index++] = (lowBits >> 16) & 0xff;
+ buffer[index++] = (lowBits >> 24) & 0xff;
+ // Encode high bits
+ buffer[index++] = highBits & 0xff;
+ buffer[index++] = (highBits >> 8) & 0xff;
+ buffer[index++] = (highBits >> 16) & 0xff;
+ buffer[index++] = (highBits >> 24) & 0xff;
+ }
+ } else {
+ // Encode as double
+ buffer[index++] = BSON.BSON_DATA_NUMBER;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ // Write float
+ ieee754.writeIEEE754(buffer, value, index, 'little', 52, 8);
+ // Ajust index
+ index = index + 8;
+ }
+
+ return index;
+ case 'undefined':
+ // Set long type
+ buffer[index++] = BSON.BSON_DATA_NULL;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ return index;
+ case 'boolean':
+ // Write the type
+ buffer[index++] = BSON.BSON_DATA_BOOLEAN;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ // Encode the boolean value
+ buffer[index++] = value ? 1 : 0;
+ return index;
+ case 'object':
+ if(value === null || value instanceof MinKey || value instanceof MaxKey
+ || value['_bsontype'] == 'MinKey' || value['_bsontype'] == 'MaxKey') {
+ // Write the type of either min or max key
+ if(value === null) {
+ buffer[index++] = BSON.BSON_DATA_NULL;
+ } else if(value instanceof MinKey) {
+ buffer[index++] = BSON.BSON_DATA_MIN_KEY;
+ } else {
+ buffer[index++] = BSON.BSON_DATA_MAX_KEY;
+ }
+
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ return index;
+ } else if(value instanceof ObjectID || value['_bsontype'] == 'ObjectID') {
+ // Write the type
+ buffer[index++] = BSON.BSON_DATA_OID;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ // Write objectid
+ buffer.write(value.id, index, 'binary');
+ // Ajust index
+ index = index + 12;
+ return index;
+ } else if(value instanceof Date) {
+ // Write the type
+ buffer[index++] = BSON.BSON_DATA_DATE;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+
+ // Write the date
+ var dateInMilis = Long.fromNumber(value.getTime());
+ var lowBits = dateInMilis.getLowBits();
+ var highBits = dateInMilis.getHighBits();
+ // Encode low bits
+ buffer[index++] = lowBits & 0xff;
+ buffer[index++] = (lowBits >> 8) & 0xff;
+ buffer[index++] = (lowBits >> 16) & 0xff;
+ buffer[index++] = (lowBits >> 24) & 0xff;
+ // Encode high bits
+ buffer[index++] = highBits & 0xff;
+ buffer[index++] = (highBits >> 8) & 0xff;
+ buffer[index++] = (highBits >> 16) & 0xff;
+ buffer[index++] = (highBits >> 24) & 0xff;
+ return index;
+ } else if(Buffer.isBuffer(value)) {
+ // Write the type
+ buffer[index++] = BSON.BSON_DATA_BINARY;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ // Get size of the buffer (current write point)
+ var size = value.length;
+ // Write the size of the string to buffer
+ buffer[index++] = size & 0xff;
+ buffer[index++] = (size >> 8) & 0xff;
+ buffer[index++] = (size >> 16) & 0xff;
+ buffer[index++] = (size >> 24) & 0xff;
+ // Write the default subtype
+ buffer[index++] = BSON.BSON_BINARY_SUBTYPE_DEFAULT;
+ // Copy the content form the binary field to the buffer
+ value.copy(buffer, index, 0, size);
+ // Adjust the index
+ index = index + size;
+ return index;
+ } else if(value instanceof Long || value instanceof Timestamp || value['_bsontype'] == 'Long' || value['_bsontype'] == 'Timestamp') {
+ // Write the type
+ buffer[index++] = value instanceof Long ? BSON.BSON_DATA_LONG : BSON.BSON_DATA_TIMESTAMP;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ // Write the date
+ var lowBits = value.getLowBits();
+ var highBits = value.getHighBits();
+ // Encode low bits
+ buffer[index++] = lowBits & 0xff;
+ buffer[index++] = (lowBits >> 8) & 0xff;
+ buffer[index++] = (lowBits >> 16) & 0xff;
+ buffer[index++] = (lowBits >> 24) & 0xff;
+ // Encode high bits
+ buffer[index++] = highBits & 0xff;
+ buffer[index++] = (highBits >> 8) & 0xff;
+ buffer[index++] = (highBits >> 16) & 0xff;
+ buffer[index++] = (highBits >> 24) & 0xff;
+ return index;
+ } else if(value instanceof Double || value['_bsontype'] == 'Double') {
+ // Encode as double
+ buffer[index++] = BSON.BSON_DATA_NUMBER;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ // Write float
+ ieee754.writeIEEE754(buffer, value, index, 'little', 52, 8);
+ // Ajust index
+ index = index + 8;
+ return index;
+ } else if(value instanceof Code || value['_bsontype'] == 'Code') {
+ if(value.scope != null && Object.keys(value.scope).length > 0) {
+ // Write the type
+ buffer[index++] = BSON.BSON_DATA_CODE_W_SCOPE;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ // Calculate the scope size
+ var scopeSize = BSON.calculateObjectSize(value.scope);
+ // Function string
+ var functionString = value.code.toString();
+ // Function Size
+ var codeSize = Buffer.byteLength(functionString) + 1;
+
+ // Calculate full size of the object
+ var totalSize = 4 + codeSize + scopeSize + 4;
+
+ // Write the total size of the object
+ buffer[index++] = totalSize & 0xff;
+ buffer[index++] = (totalSize >> 8) & 0xff;
+ buffer[index++] = (totalSize >> 16) & 0xff;
+ buffer[index++] = (totalSize >> 24) & 0xff;
+
+ // Write the size of the string to buffer
+ buffer[index++] = codeSize & 0xff;
+ buffer[index++] = (codeSize >> 8) & 0xff;
+ buffer[index++] = (codeSize >> 16) & 0xff;
+ buffer[index++] = (codeSize >> 24) & 0xff;
+
+ // Write the string
+ buffer.write(functionString, index, 'utf8');
+ // Update index
+ index = index + codeSize - 1;
+ // Write zero
+ buffer[index++] = 0;
+ // Serialize the scope object
+ var scopeObjectBuffer = new Buffer(scopeSize);
+ // Execute the serialization into a seperate buffer
+ serializeObject(value.scope, checkKeys, scopeObjectBuffer, 0, serializeFunctions);
+
+ // Adjusted scope Size (removing the header)
+ var scopeDocSize = scopeSize;
+ // Write scope object size
+ buffer[index++] = scopeDocSize & 0xff;
+ buffer[index++] = (scopeDocSize >> 8) & 0xff;
+ buffer[index++] = (scopeDocSize >> 16) & 0xff;
+ buffer[index++] = (scopeDocSize >> 24) & 0xff;
+
+ // Write the scopeObject into the buffer
+ scopeObjectBuffer.copy(buffer, index, 0, scopeSize);
+
+ // Adjust index, removing the empty size of the doc (5 bytes 0000000005)
+ index = index + scopeDocSize - 5;
+ // Write trailing zero
+ buffer[index++] = 0;
+ return index
+ } else {
+ buffer[index++] = BSON.BSON_DATA_CODE;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ // Function string
+ var functionString = value.code.toString();
+ // Function Size
+ var size = Buffer.byteLength(functionString) + 1;
+ // Write the size of the string to buffer
+ buffer[index++] = size & 0xff;
+ buffer[index++] = (size >> 8) & 0xff;
+ buffer[index++] = (size >> 16) & 0xff;
+ buffer[index++] = (size >> 24) & 0xff;
+ // Write the string
+ buffer.write(functionString, index, 'utf8');
+ // Update index
+ index = index + size - 1;
+ // Write zero
+ buffer[index++] = 0;
+ return index;
+ }
+ } else if(value instanceof Binary || value['_bsontype'] == 'Binary') {
+ // Write the type
+ buffer[index++] = BSON.BSON_DATA_BINARY;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ // Extract the buffer
+ var data = value.value(true);
+ // Calculate size
+ var size = value.position;
+ // Write the size of the string to buffer
+ buffer[index++] = size & 0xff;
+ buffer[index++] = (size >> 8) & 0xff;
+ buffer[index++] = (size >> 16) & 0xff;
+ buffer[index++] = (size >> 24) & 0xff;
+ // Write the subtype to the buffer
+ buffer[index++] = value.sub_type;
+ // Write the data to the object
+ data.copy(buffer, index, 0, value.position);
+ // Ajust index
+ index = index + value.position;
+ return index;
+ } else if(value instanceof Symbol || value['_bsontype'] == 'Symbol') {
+ // Write the type
+ buffer[index++] = BSON.BSON_DATA_SYMBOL;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ // Calculate size
+ size = Buffer.byteLength(value.value) + 1;
+ // Write the size of the string to buffer
+ buffer[index++] = size & 0xff;
+ buffer[index++] = (size >> 8) & 0xff;
+ buffer[index++] = (size >> 16) & 0xff;
+ buffer[index++] = (size >> 24) & 0xff;
+ // Write the string
+ buffer.write(value.value, index, 'utf8');
+ // Update index
+ index = index + size - 1;
+ // Write zero
+ buffer[index++] = 0x00;
+ return index;
+ } else if(value instanceof DBRef || value['_bsontype'] == 'DBRef') {
+ // Write the type
+ buffer[index++] = BSON.BSON_DATA_OBJECT;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ // Set up correct object for serialization
+ var ordered_values = {
+ '$ref': value.namespace
+ , '$id' : value.oid
+ };
+
+ // Add db reference if it exists
+ if(null != value.db) {
+ ordered_values['$db'] = value.db;
+ }
+
+ // Message size
+ var size = BSON.calculateObjectSize(ordered_values);
+ // Serialize the object
+ var endIndex = BSON.serializeWithBufferAndIndex(ordered_values, checkKeys, buffer, index, serializeFunctions);
+ // Write the size of the string to buffer
+ buffer[index++] = size & 0xff;
+ buffer[index++] = (size >> 8) & 0xff;
+ buffer[index++] = (size >> 16) & 0xff;
+ buffer[index++] = (size >> 24) & 0xff;
+ // Write zero for object
+ buffer[endIndex++] = 0x00;
+ // Return the end index
+ return endIndex;
+ } else if(value instanceof RegExp || toString.call(value) === '[object RegExp]') {
+ // Write the type
+ buffer[index++] = BSON.BSON_DATA_REGEXP;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+
+ // Write the regular expression string
+ buffer.write(value.source, index, 'utf8');
+ // Adjust the index
+ index = index + Buffer.byteLength(value.source);
+ // Write zero
+ buffer[index++] = 0x00;
+ // Write the parameters
+ if(value.global) buffer[index++] = 0x73; // s
+ if(value.ignoreCase) buffer[index++] = 0x69; // i
+ if(value.multiline) buffer[index++] = 0x6d; // m
+ // Add ending zero
+ buffer[index++] = 0x00;
+ return index;
+ } else {
+ // Write the type
+ buffer[index++] = Array.isArray(value) ? BSON.BSON_DATA_ARRAY : BSON.BSON_DATA_OBJECT;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ // Serialize the object
+ var endIndex = serializeObject(value, checkKeys, buffer, index + 4, serializeFunctions);
+ // Write size
+ var size = endIndex - index;
+ // Write the size of the string to buffer
+ buffer[index++] = size & 0xff;
+ buffer[index++] = (size >> 8) & 0xff;
+ buffer[index++] = (size >> 16) & 0xff;
+ buffer[index++] = (size >> 24) & 0xff;
+ return endIndex;
+ }
+ case 'function':
+ // WTF for 0.4.X where typeof /someregexp/ === 'function'
+ if(value instanceof RegExp || toString.call(value) === '[object RegExp]') {
+ // Write the type
+ buffer[index++] = BSON.BSON_DATA_REGEXP;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+
+ // Write the regular expression string
+ buffer.write(value.source, index, 'utf8');
+ // Adjust the index
+ index = index + Buffer.byteLength(value.source);
+ // Write zero
+ buffer[index++] = 0x00;
+ // Write the parameters
+ if(value.global) buffer[index++] = 0x73; // s
+ if(value.ignoreCase) buffer[index++] = 0x69; // i
+ if(value.multiline) buffer[index++] = 0x6d; // m
+ // Add ending zero
+ buffer[index++] = 0x00;
+ return index;
+ } else {
+ if(serializeFunctions && value.scope != null && Object.keys(value.scope).length > 0) {
+ // Write the type
+ buffer[index++] = BSON.BSON_DATA_CODE_W_SCOPE;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ // Calculate the scope size
+ var scopeSize = BSON.calculateObjectSize(value.scope);
+ // Function string
+ var functionString = value.toString();
+ // Function Size
+ var codeSize = Buffer.byteLength(functionString) + 1;
+
+ // Calculate full size of the object
+ var totalSize = 4 + codeSize + scopeSize;
+
+ // Write the total size of the object
+ buffer[index++] = totalSize & 0xff;
+ buffer[index++] = (totalSize >> 8) & 0xff;
+ buffer[index++] = (totalSize >> 16) & 0xff;
+ buffer[index++] = (totalSize >> 24) & 0xff;
+
+ // Write the size of the string to buffer
+ buffer[index++] = codeSize & 0xff;
+ buffer[index++] = (codeSize >> 8) & 0xff;
+ buffer[index++] = (codeSize >> 16) & 0xff;
+ buffer[index++] = (codeSize >> 24) & 0xff;
+
+ // Write the string
+ buffer.write(functionString, index, 'utf8');
+ // Update index
+ index = index + codeSize - 1;
+ // Write zero
+ buffer[index++] = 0;
+ // Serialize the scope object
+ var scopeObjectBuffer = new Buffer(scopeSize);
+ // Execute the serialization into a seperate buffer
+ serializeObject(value.scope, checkKeys, scopeObjectBuffer, 0, serializeFunctions);
+
+ // Adjusted scope Size (removing the header)
+ var scopeDocSize = scopeSize - 4;
+ // Write scope object size
+ buffer[index++] = scopeDocSize & 0xff;
+ buffer[index++] = (scopeDocSize >> 8) & 0xff;
+ buffer[index++] = (scopeDocSize >> 16) & 0xff;
+ buffer[index++] = (scopeDocSize >> 24) & 0xff;
+
+ // Write the scopeObject into the buffer
+ scopeObjectBuffer.copy(buffer, index, 0, scopeSize);
+
+ // Adjust index, removing the empty size of the doc (5 bytes 0000000005)
+ index = index + scopeDocSize - 5;
+ // Write trailing zero
+ buffer[index++] = 0;
+ return index
+ } else if(serializeFunctions) {
+ buffer[index++] = BSON.BSON_DATA_CODE;
+ // Encode the name
+ index = index + buffer.write(name, index, 'utf8') + 1;
+ buffer[index - 1] = 0;
+ // Function string
+ var functionString = value.toString();
+ // Function Size
+ var size = Buffer.byteLength(functionString) + 1;
+ // Write the size of the string to buffer
+ buffer[index++] = size & 0xff;
+ buffer[index++] = (size >> 8) & 0xff;
+ buffer[index++] = (size >> 16) & 0xff;
+ buffer[index++] = (size >> 24) & 0xff;
+ // Write the string
+ buffer.write(functionString, index, 'utf8');
+ // Update index
+ index = index + size - 1;
+ // Write zero
+ buffer[index++] = 0;
+ return index;
+ }
+ }
+ }
+
+ // If no value to serialize
+ return index;
+}
+
+/**
+ * Serialize a Javascript object.
+ *
+ * @param {Object} object the Javascript object to serialize.
+ * @param {Boolean} checkKeys the serializer will check if keys are valid.
+ * @param {Boolean} asBuffer return the serialized object as a Buffer object **(ignore)**.
+ * @param {Boolean} serializeFunctions serialize the javascript functions **(default:false)**.
+ * @return {Buffer} returns the Buffer object containing the serialized object.
+ * @api public
+ */
+BSON.serialize = function(object, checkKeys, asBuffer, serializeFunctions) {
+ var buffer = new Buffer(BSON.calculateObjectSize(object, serializeFunctions));
+ BSON.serializeWithBufferAndIndex(object, checkKeys, buffer, 0, serializeFunctions);
+ return buffer;
+}
+
+/**
+ * Contains the function cache if we have that enable to allow for avoiding the eval step on each deserialization, comparison is by md5
+ *
+ * @ignore
+ * @api private
+ */
+var functionCache = BSON.functionCache = {};
+
+/**
+ * Crc state variables shared by function
+ *
+ * @ignore
+ * @api private
+ */
+var table = [0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D];
+
+/**
+ * CRC32 hash method, Fast and enough versitility for our usage
+ *
+ * @ignore
+ * @api private
+ */
+var crc32 = function(string, start, end) {
+ var crc = 0
+ var x = 0;
+ var y = 0;
+ crc = crc ^ (-1);
+
+ for(var i = start, iTop = end; i < iTop;i++) {
+ y = (crc ^ string[i]) & 0xFF;
+ x = table[y];
+ crc = (crc >>> 8) ^ x;
+ }
+
+ return crc ^ (-1);
+}
+
+/**
+ * Deserialize stream data as BSON documents.
+ *
+ * Options
+ * - **evalFunctions** {Boolean, default:false}, evaluate functions in the BSON document scoped to the object deserialized.
+ * - **cacheFunctions** {Boolean, default:false}, cache evaluated functions for reuse.
+ * - **cacheFunctionsCrc32** {Boolean, default:false}, use a crc32 code for caching, otherwise use the string of the function.
+ *
+ * @param {Buffer} data the buffer containing the serialized set of BSON documents.
+ * @param {Number} startIndex the start index in the data Buffer where the deserialization is to start.
+ * @param {Number} numberOfDocuments number of documents to deserialize.
+ * @param {Array} documents an array where to store the deserialized documents.
+ * @param {Number} docStartIndex the index in the documents array from where to start inserting documents.
+ * @param {Object} [options] additional options used for the deserialization.
+ * @return {Number} returns the next index in the buffer after deserialization **x** numbers of documents.
+ * @api public
+ */
+BSON.deserializeStream = function(data, startIndex, numberOfDocuments, documents, docStartIndex, options) {
+ // if(numberOfDocuments !== documents.length) throw new Error("Number of expected results back is less than the number of documents");
+ options = options != null ? options : {};
+ var index = startIndex;
+ // Loop over all documents
+ for(var i = 0; i < numberOfDocuments; i++) {
+ // Find size of the document
+ var size = data[index] | data[index + 1] << 8 | data[index + 2] << 16 | data[index + 3] << 24;
+ // Update options with index
+ options['index'] = index;
+ // Parse the document at this point
+ documents[docStartIndex + i] = BSON.deserialize(data, options);
+ // Adjust index by the document size
+ index = index + size;
+ }
+
+ // Return object containing end index of parsing and list of documents
+ return index;
+}
+
+/**
+ * Ensure eval is isolated.
+ *
+ * @ignore
+ * @api private
+ */
+var isolateEvalWithHash = function(functionCache, hash, functionString, object) {
+ // Contains the value we are going to set
+ var value = null;
+
+ // Check for cache hit, eval if missing and return cached function
+ if(functionCache[hash] == null) {
+ eval("value = " + functionString);
+ functionCache[hash] = value;
+ }
+ // Set the object
+ return functionCache[hash].bind(object);
+}
+
+/**
+ * Ensure eval is isolated.
+ *
+ * @ignore
+ * @api private
+ */
+var isolateEval = function(functionString) {
+ // Contains the value we are going to set
+ var value = null;
+ // Eval the function
+ eval("value = " + functionString);
+ return value;
+}
+
+/**
+ * Deserialize data as BSON.
+ *
+ * Options
+ * - **evalFunctions** {Boolean, default:false}, evaluate functions in the BSON document scoped to the object deserialized.
+ * - **cacheFunctions** {Boolean, default:false}, cache evaluated functions for reuse.
+ * - **cacheFunctionsCrc32** {Boolean, default:false}, use a crc32 code for caching, otherwise use the string of the function.
+ *
+ * @param {Buffer} buffer the buffer containing the serialized set of BSON documents.
+ * @param {Object} [options] additional options used for the deserialization.
+ * @param {Boolean} [isArray] ignore used for recursive parsing.
+ * @return {Object} returns the deserialized Javascript Object.
+ * @api public
+ */
+BSON.deserialize = function(buffer, options, isArray) {
+ // Options
+ options = options == null ? {} : options;
+ var evalFunctions = options['evalFunctions'] == null ? false : options['evalFunctions'];
+ var cacheFunctions = options['cacheFunctions'] == null ? false : options['cacheFunctions'];
+ var cacheFunctionsCrc32 = options['cacheFunctionsCrc32'] == null ? false : options['cacheFunctionsCrc32'];
+
+ // Validate that we have at least 4 bytes of buffer
+ if(buffer.length < 5) throw new Error("corrupt bson message < 5 bytes long");
+
+ // Set up index
+ var index = typeof options['index'] == 'number' ? options['index'] : 0;
+ // Reads in a C style string
+ var readCStyleString = function() {
+ // Get the start search index
+ var i = index;
+ // Locate the end of the c string
+ while(buffer[i] !== 0x00) { i++ }
+ // Grab utf8 encoded string
+ var string = buffer.toString('utf8', index, i);
+ // Update index position
+ index = i + 1;
+ // Return string
+ return string;
+ }
+
+ // Create holding object
+ var object = isArray ? [] : {};
+
+ // Read the document size
+ var size = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
+
+ // Ensure buffer is valid size
+ if(size < 0 || size > buffer.length) throw new Error("corrupt bson message");
+
+ // While we have more left data left keep parsing
+ while(true) {
+ // Read the type
+ var elementType = buffer[index++];
+ // If we get a zero it's the last byte, exit
+ if(elementType == 0) break;
+ // Read the name of the field
+ var name = readCStyleString();
+ // Switch on the type
+ switch(elementType) {
+ case BSON.BSON_DATA_OID:
+ // Decode the oid
+ object[name] = new ObjectID(buffer.toString('binary', index, index + 12));
+ // Update index
+ index = index + 12;
+ break;
+ case BSON.BSON_DATA_STRING:
+ // Read the content of the field
+ var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
+ // Add string to object
+ object[name] = buffer.toString('utf8', index, index + stringSize - 1);
+ // Update parse index position
+ index = index + stringSize;
+ break;
+ case BSON.BSON_DATA_INT:
+ // Decode the 32bit value
+ object[name] = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
+ break;
+ case BSON.BSON_DATA_NUMBER:
+ // Decode the double value
+ object[name] = ieee754.readIEEE754(buffer, index, 'little', 52, 8);
+ // Update the index
+ index = index + 8;
+ break;
+ case BSON.BSON_DATA_DATE:
+ // Unpack the low and high bits
+ var lowBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
+ var highBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
+ // Set date object
+ object[name] = new Date(new Long(lowBits, highBits).toNumber());
+ break;
+ case BSON.BSON_DATA_BOOLEAN:
+ // Parse the boolean value
+ object[name] = buffer[index++] == 1;
+ break;
+ case BSON.BSON_DATA_NULL:
+ // Parse the boolean value
+ object[name] = null;
+ break;
+ case BSON.BSON_DATA_BINARY:
+ // Decode the size of the binary blob
+ var binarySize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
+ // Decode the subtype
+ var subType = buffer[index++];
+ // Decode as raw Buffer object if options specifies it
+ object[name] = new Binary(buffer.slice(index, index + binarySize), subType);
+ // Update the index
+ index = index + binarySize;
+ break;
+ case BSON.BSON_DATA_ARRAY:
+ options['index'] = index;
+ // Decode the size of the array document
+ var objectSize = buffer[index] | buffer[index + 1] << 8 | buffer[index + 2] << 16 | buffer[index + 3] << 24;
+ // Set the array to the object
+ object[name] = BSON.deserialize(buffer, options, true);
+ // Adjust the index
+ index = index + objectSize;
+ break;
+ case BSON.BSON_DATA_OBJECT:
+ options['index'] = index;
+ // Decode the size of the object document
+ var objectSize = buffer[index] | buffer[index + 1] << 8 | buffer[index + 2] << 16 | buffer[index + 3] << 24;
+ // Set the array to the object
+ object[name] = BSON.deserialize(buffer, options, false);
+ // Adjust the index
+ index = index + objectSize;
+ break;
+ case BSON.BSON_DATA_REGEXP:
+ // Create the regexp
+ var source = readCStyleString();
+ var regExpOptions = readCStyleString();
+ // For each option add the corresponding one for javascript
+ var optionsArray = new Array(regExpOptions.length);
+
+ // Parse options
+ for(var i = 0; i < regExpOptions.length; i++) {
+ switch(regExpOptions[i]) {
+ case 'm':
+ optionsArray[i] = 'm';
+ break;
+ case 's':
+ optionsArray[i] = 'g';
+ break;
+ case 'i':
+ optionsArray[i] = 'i';
+ break;
+ }
+ }
+
+ object[name] = new RegExp(source, optionsArray.join(''));
+ break;
+ case BSON.BSON_DATA_LONG:
+ // Unpack the low and high bits
+ var lowBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
+ var highBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
+ // Create long object
+ var long = new Long(lowBits, highBits);
+ // Set the object
+ object[name] = long.lessThanOrEqual(JS_INT_MAX_LONG) && long.greaterThanOrEqual(JS_INT_MIN_LONG) ? long.toNumber() : long;
+ break;
+ case BSON.BSON_DATA_SYMBOL:
+ // Read the content of the field
+ var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
+ // Add string to object
+ object[name] = new Symbol(buffer.toString('utf8', index, index + stringSize - 1));
+ // Update parse index position
+ index = index + stringSize;
+ break;
+ case BSON.BSON_DATA_TIMESTAMP:
+ // Unpack the low and high bits
+ var lowBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
+ var highBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
+ // Set the object
+ object[name] = new Timestamp(lowBits, highBits);
+ break;
+ case BSON.BSON_DATA_MIN_KEY:
+ // Parse the object
+ object[name] = new MinKey();
+ break;
+ case BSON.BSON_DATA_MAX_KEY:
+ // Parse the object
+ object[name] = new MaxKey();
+ break;
+ case BSON.BSON_DATA_CODE:
+ // Read the content of the field
+ var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
+ // Function string
+ var functionString = buffer.toString('utf8', index, index + stringSize - 1);
+
+ // If we are evaluating the functions
+ if(evalFunctions) {
+ // Contains the value we are going to set
+ var value = null;
+ // If we have cache enabled let's look for the md5 of the function in the cache
+ if(cacheFunctions) {
+ var hash = cacheFunctionsCrc32 ? crc32(functionString) : functionString;
+ // Got to do this to avoid V8 deoptimizing the call due to finding eval
+ object[name] = isolateEvalWithHash(functionCache, hash, functionString, object);
+ } else {
+ // Set directly
+ object[name] = isolateEval(functionString);
+ }
+ } else {
+ object[name] = new Code(functionString, {});
+ }
+
+ // Update parse index position
+ index = index + stringSize;
+ break;
+ case BSON.BSON_DATA_CODE_W_SCOPE:
+ // Read the content of the field
+ var totalSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
+ var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24;
+ // Javascript function
+ var functionString = buffer.toString('utf8', index, index + stringSize - 1);
+ // Update parse index position
+ index = index + stringSize;
+ // Parse the element
+ options['index'] = index;
+ // Decode the size of the object document
+ var objectSize = buffer[index] | buffer[index + 1] << 8 | buffer[index + 2] << 16 | buffer[index + 3] << 24;
+ // Decode the scope object
+ var scopeObject = BSON.deserialize(buffer, options, false);
+ // Adjust the index
+ index = index + objectSize;
+
+ // If we are evaluating the functions
+ if(evalFunctions) {
+ // Contains the value we are going to set
+ var value = null;
+ // If we have cache enabled let's look for the md5 of the function in the cache
+ if(cacheFunctions) {
+ var hash = cacheFunctionsCrc32 ? crc32(functionString) : functionString;
+ // Got to do this to avoid V8 deoptimizing the call due to finding eval
+ object[name] = isolateEvalWithHash(functionCache, hash, functionString, object);
+ } else {
+ // Set directly
+ object[name] = isolateEval(functionString);
+ }
+
+ // Set the scope on the object
+ object[name].scope = scopeObject;
+ } else {
+ object[name] = new Code(functionString, scopeObject);
+ }
+
+ // Add string to object
+ break;
+ }
+ }
+
+ // Check if we have a db ref object
+ if(object['$id'] != null) object = new DBRef(object['$ref'], object['$id'], object['$db']);
+
+ // Return the final objects
+ return object;
+}
+
+/**
+ * Check if key name is valid.
+ *
+ * @ignore
+ * @api private
+ */
+BSON.checkKey = function checkKey (key) {
+ if (!key.length) return;
+ // Check if we have a legal key for the object
+ if('$' == key[0]) {
+ throw Error("key " + key + " must not start with '$'");
+ } else if (!!~key.indexOf('.')) {
+ throw Error("key " + key + " must not contain '.'");
+ }
+};
+
+/**
+ * Deserialize data as BSON.
+ *
+ * Options
+ * - **evalFunctions** {Boolean, default:false}, evaluate functions in the BSON document scoped to the object deserialized.
+ * - **cacheFunctions** {Boolean, default:false}, cache evaluated functions for reuse.
+ * - **cacheFunctionsCrc32** {Boolean, default:false}, use a crc32 code for caching, otherwise use the string of the function.
+ *
+ * @param {Buffer} buffer the buffer containing the serialized set of BSON documents.
+ * @param {Object} [options] additional options used for the deserialization.
+ * @param {Boolean} [isArray] ignore used for recursive parsing.
+ * @return {Object} returns the deserialized Javascript Object.
+ * @api public
+ */
+BSON.prototype.deserialize = function(data, options) {
+ return BSON.deserialize(data, options);
+}
+
+/**
+ * Deserialize stream data as BSON documents.
+ *
+ * Options
+ * - **evalFunctions** {Boolean, default:false}, evaluate functions in the BSON document scoped to the object deserialized.
+ * - **cacheFunctions** {Boolean, default:false}, cache evaluated functions for reuse.
+ * - **cacheFunctionsCrc32** {Boolean, default:false}, use a crc32 code for caching, otherwise use the string of the function.
+ *
+ * @param {Buffer} data the buffer containing the serialized set of BSON documents.
+ * @param {Number} startIndex the start index in the data Buffer where the deserialization is to start.
+ * @param {Number} numberOfDocuments number of documents to deserialize.
+ * @param {Array} documents an array where to store the deserialized documents.
+ * @param {Number} docStartIndex the index in the documents array from where to start inserting documents.
+ * @param {Object} [options] additional options used for the deserialization.
+ * @return {Number} returns the next index in the buffer after deserialization **x** numbers of documents.
+ * @api public
+ */
+BSON.prototype.deserializeStream = function(data, startIndex, numberOfDocuments, documents, docStartIndex, options) {
+ return BSON.deserializeStream(data, startIndex, numberOfDocuments, documents, docStartIndex, options);
+}
+
+/**
+ * Serialize a Javascript object.
+ *
+ * @param {Object} object the Javascript object to serialize.
+ * @param {Boolean} checkKeys the serializer will check if keys are valid.
+ * @param {Boolean} asBuffer return the serialized object as a Buffer object **(ignore)**.
+ * @param {Boolean} serializeFunctions serialize the javascript functions **(default:false)**.
+ * @return {Buffer} returns the Buffer object containing the serialized object.
+ * @api public
+ */
+BSON.prototype.serialize = function(object, checkKeys, asBuffer, serializeFunctions) {
+ return BSON.serialize(object, checkKeys, asBuffer, serializeFunctions);
+}
+
+/**
+ * Calculate the bson size for a passed in Javascript object.
+ *
+ * @param {Object} object the Javascript object to calculate the BSON byte size for.
+ * @param {Boolean} [serializeFunctions] serialize all functions in the object **(default:false)**.
+ * @return {Number} returns the number of bytes the BSON object will take up.
+ * @api public
+ */
+BSON.prototype.calculateObjectSize = function(object, serializeFunctions) {
+ return BSON.calculateObjectSize(object, serializeFunctions);
+}
+
+/**
+ * Serialize a Javascript object using a predefined Buffer and index into the buffer, useful when pre-allocating the space for serialization.
+ *
+ * @param {Object} object the Javascript object to serialize.
+ * @param {Boolean} checkKeys the serializer will check if keys are valid.
+ * @param {Buffer} buffer the Buffer you pre-allocated to store the serialized BSON object.
+ * @param {Number} index the index in the buffer where we wish to start serializing into.
+ * @param {Boolean} serializeFunctions serialize the javascript functions **(default:false)**.
+ * @return {Number} returns the new write index in the Buffer.
+ * @api public
+ */
+BSON.prototype.serializeWithBufferAndIndex = function(object, checkKeys, buffer, startIndex, serializeFunctions) {
+ return BSON.serializeWithBufferAndIndex(object, checkKeys, buffer, startIndex, serializeFunctions);
+}
+
+/**
+ * @ignore
+ * @api private
+ */
+if(typeof window === 'undefined') {
+ exports.Code = Code;
+ exports.Symbol = Symbol;
+ exports.BSON = BSON;
+ exports.DBRef = DBRef;
+ exports.Binary = Binary;
+ exports.ObjectID = ObjectID;
+ exports.Long = Long;
+ exports.Timestamp = Timestamp;
+ exports.Double = Double;
+ exports.MinKey = MinKey;
+ exports.MaxKey = MaxKey;
+}
diff --git a/public/code.js b/public/code.js
new file mode 100644
index 0000000..cc951c1
--- /dev/null
+++ b/public/code.js
@@ -0,0 +1,25 @@
+/**
+ * A class representation of the BSON Code type.
+ *
+ * @class Represents the BSON Code type.
+ * @param {String|Function} code a string or function.
+ * @param {Object} [scope] an optional scope for the function.
+ * @return {Code}
+ */
+function Code(code, scope) {
+ this._bsontype = 'Code';
+ this.code = code;
+ this.scope = scope == null ? {} : scope;
+};
+
+/**
+ * @ignore
+ * @api private
+ */
+Code.prototype.toJSON = function() {
+ return {scope:this.scope, code:this.code};
+}
+
+if(typeof window === 'undefined') {
+ exports.Code = Code;
+}
\ No newline at end of file
diff --git a/public/communication.js b/public/communication.js
new file mode 100644
index 0000000..d1d10a4
--- /dev/null
+++ b/public/communication.js
@@ -0,0 +1,66 @@
+// Handles all our communication
+var GameCommunication = function() {
+ // Set up the object handling all the communication
+ // with the backend server
+ // this.messageHandler = {
+ // initCommands: this.initCommands.bind(this)
+ // };
+}
+
+// Handle the setup of the connection
+GameCommunication.prototype.connect = function(callback) {
+ var url = "ws://" + document.URL.substr(7).split('/')[0];
+ // Create a websocket either using the Mozilla web socket or the
+ // standard one
+ var wsCtor = window['MozWebSocket'] ? MozWebSocket : WebSocket;
+ this.socket = new wsCtor(url, 'game');
+ this.socket.binaryType = 'blob';
+
+ // Bind the handlers
+ this.socket.onopen = callback;
+ this.socket.onerror = callback;
+ this.socket.onmessage = this.handleWebsocketMessage.bind(this);
+ this.socket.onclose = this.handleWebsocketClose.bind(this);
+
+ // Contains all the incoming messages
+ // We only access messages during the rendering phase of the game
+ this.messages = [];
+}
+
+// Dispatch a command
+GameCommunication.prototype.dispatchCommand = function(command) {
+ console.log("=============== command :: " + (command instanceof ArrayBuffer))
+
+ if(command instanceof ArrayBuffer) {
+ console.log("---------------------------------------- ArrayBuffer")
+
+ this.socket.send(command);
+ } else if(typeof command == 'object') {
+ console.log("---------------------------------------- Object")
+
+ this.socket.send(JSON.stringify(command));
+ }
+}
+
+// Open the connection
+GameCommunication.prototype.handleWebsocketOpen = function() {
+ console.log("==================================== socket opened")
+}
+
+// Handle the web socket messages
+GameCommunication.prototype.handleWebsocketMessage = function(message) {
+ // Let's add the message to the incoming message array
+ // this.messages.push(message);
+ console.log("==================================== received message")
+ console.dir(message)
+}
+
+// Close the connection
+GameCommunication.prototype.handleWebsocketClose = function() {
+ console.log("==================================== socket closed")
+}
+
+
+
+
+
diff --git a/public/db_ref.js b/public/db_ref.js
new file mode 100644
index 0000000..7961ba0
--- /dev/null
+++ b/public/db_ref.js
@@ -0,0 +1,31 @@
+/**
+ * A class representation of the BSON DBRef type.
+ *
+ * @class Represents the BSON DBRef type.
+ * @param {String} namespace the collection name.
+ * @param {ObjectID} oid the reference ObjectID.
+ * @param {String} [db] optional db name, if omitted the reference is local to the current db.
+ * @return {DBRef}
+ */
+function DBRef(namespace, oid, db) {
+ this._bsontype = 'DBRef';
+ this.namespace = namespace;
+ this.oid = oid;
+ this.db = db;
+};
+
+/**
+ * @ignore
+ * @api private
+ */
+DBRef.prototype.toJSON = function() {
+ return {
+ '$ref':this.namespace,
+ '$id':this.oid,
+ '$db':this.db == null ? '' : this.db
+ };
+}
+
+if(typeof window === 'undefined') {
+ exports.DBRef = DBRef;
+}
\ No newline at end of file
diff --git a/public/double.js b/public/double.js
new file mode 100644
index 0000000..173ac0c
--- /dev/null
+++ b/public/double.js
@@ -0,0 +1,33 @@
+/**
+ * A class representation of the BSON Double type.
+ *
+ * @class Represents the BSON Double type.
+ * @param {Number} value the number we want to represent as a double.
+ * @return {Double}
+ */
+function Double(value) {
+ this._bsontype = 'Double';
+ this.value = value;
+}
+
+/**
+ * Access the number value.
+ *
+ * @return {Number} returns the wrapped double number.
+ * @api public
+ */
+Double.prototype.valueOf = function() {
+ return this.value;
+};
+
+/**
+ * @ignore
+ * @api private
+ */
+Double.prototype.toJSON = function() {
+ return this.value;
+}
+
+if(typeof window === 'undefined') {
+ exports.Double = Double;
+}
\ No newline at end of file
diff --git a/public/float_parser.js b/public/float_parser.js
new file mode 100644
index 0000000..7c12fe5
--- /dev/null
+++ b/public/float_parser.js
@@ -0,0 +1,123 @@
+// Copyright (c) 2008, Fair Oaks Labs, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// * Neither the name of Fair Oaks Labs, Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+//
+// Modifications to writeIEEE754 to support negative zeroes made by Brian White
+
+var readIEEE754 = function(buffer, offset, endian, mLen, nBytes) {
+ var e, m,
+ bBE = (endian === 'big'),
+ eLen = nBytes * 8 - mLen - 1,
+ eMax = (1 << eLen) - 1,
+ eBias = eMax >> 1,
+ nBits = -7,
+ i = bBE ? 0 : (nBytes - 1),
+ d = bBE ? 1 : -1,
+ s = buffer[offset + i];
+
+ i += d;
+
+ e = s & ((1 << (-nBits)) - 1);
+ s >>= (-nBits);
+ nBits += eLen;
+ for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8);
+
+ m = e & ((1 << (-nBits)) - 1);
+ e >>= (-nBits);
+ nBits += mLen;
+ for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8);
+
+ if (e === 0) {
+ e = 1 - eBias;
+ } else if (e === eMax) {
+ return m ? NaN : ((s ? -1 : 1) * Infinity);
+ } else {
+ m = m + Math.pow(2, mLen);
+ e = e - eBias;
+ }
+ return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
+};
+
+var writeIEEE754 = function(buffer, value, offset, endian, mLen, nBytes) {
+ var e, m, c,
+ bBE = (endian === 'big'),
+ eLen = nBytes * 8 - mLen - 1,
+ eMax = (1 << eLen) - 1,
+ eBias = eMax >> 1,
+ rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0),
+ i = bBE ? (nBytes-1) : 0,
+ d = bBE ? -1 : 1,
+ s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
+
+ value = Math.abs(value);
+
+ if (isNaN(value) || value === Infinity) {
+ m = isNaN(value) ? 1 : 0;
+ e = eMax;
+ } else {
+ e = Math.floor(Math.log(value) / Math.LN2);
+ if (value * (c = Math.pow(2, -e)) < 1) {
+ e--;
+ c *= 2;
+ }
+ if (e+eBias >= 1) {
+ value += rt / c;
+ } else {
+ value += rt * Math.pow(2, 1 - eBias);
+ }
+ if (value * c >= 2) {
+ e++;
+ c /= 2;
+ }
+
+ if (e + eBias >= eMax) {
+ m = 0;
+ e = eMax;
+ } else if (e + eBias >= 1) {
+ m = (value * c - 1) * Math.pow(2, mLen);
+ e = e + eBias;
+ } else {
+ m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
+ e = 0;
+ }
+ }
+
+ for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8);
+
+ e = (e << mLen) | m;
+ eLen += mLen;
+ for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8);
+
+ buffer[offset + i - d] |= s * 128;
+};
+
+if(typeof window === 'undefined') {
+ exports.readIEEE754 = readIEEE754;
+ exports.writeIEEE754 = writeIEEE754;
+}
diff --git a/public/game.js b/public/game.js
new file mode 100644
index 0000000..d012ebf
--- /dev/null
+++ b/public/game.js
@@ -0,0 +1,370 @@
+// Game state variables
+var maingame = null;
+var maze = null;
+var client = null;
+
+// Load the game box
+gbox.onLoad(function () {
+ // Prepare the connection
+ client = new GameCommunication('game');
+ // Start the client
+ client.connect(function() {
+ var message = BSON.serialize({hello:"world"}, true, true, false);
+ console.log("------------------------------------------------------- :: " + message.length)
+ console.log(message)
+
+ // Dispatch a message
+ client.dispatchCommand(message.asArrayBuffer());
+
+ help.akihabaraInit({
+ title: "Bombaman",
+ splash: {footnotes: ["Music 'Only Heroes Win at Skee Ball' by Greenleo","Contact him: greenleo.bandcamp.com"] }
+ });
+
+ // Load the logo, do at the start so it will be there first for the title screen
+ gbox.addImage("logo","resources/capman/logo.png");
+ // Load the sprite sheet
+ gbox.addImage("cels","resources/capman/cels.png");
+ // Load the font set
+ gbox.addImage("font","resources/capman/font.png");
+
+ // Font are mapped over an image, setting the first letter, the letter size, the length of all rows of letters and a horizontal/vertical gap.
+ gbox.addFont({id: "small", image: "font", firstletter: " ", tileh: 8, tilew: 8, tilerow: 255, gapx: 0, gapy: 0});
+
+ // Cut up all the sprites
+ gbox.addTiles({id:"capman",image:"cels",tileh:12,tilew:12,tilerow:10,gapx:0,gapy:0});
+ gbox.addTiles({id:"ghost1",image:"cels",tileh:12,tilew:12,tilerow:3,gapx:0,gapy:12});
+ gbox.addTiles({id:"ghost2",image:"cels",tileh:12,tilew:12,tilerow:3,gapx:36,gapy:12});
+ gbox.addTiles({id:"ghost3",image:"cels",tileh:12,tilew:12,tilerow:3,gapx:36*2,gapy:12});
+ gbox.addTiles({id:"ghost4",image:"cels",tileh:12,tilew:12,tilerow:3,gapx:36*3,gapy:12});
+ gbox.addTiles({id:"ghostscared",image:"cels",tileh:12,tilew:12,tilerow:3,gapx:36*4,gapy:12});
+ gbox.addTiles({id:"ghosteaten",image:"cels",tileh:12,tilew:12,tilerow:3,gapx:36*5,gapy:12});
+ gbox.addTiles({id:"bonus",image:"cels",tileh:12,tilew:12,tilerow:8,gapx:0,gapy:24});
+ gbox.addTiles({id:"maze",image:"cels",tileh:4,tilew:4,tilerow:10,gapx:0,gapy:36});
+
+ // Now let's load some audio samples...
+ var audioserver = "resources/audio/";
+ gbox.addAudio("eat", [audioserver+"eat.mp3",audioserver+"eat.ogg"],{channel:"sfx"});
+ gbox.addAudio("eatghost", [audioserver+"laser.mp3",audioserver+"laser.ogg"],{channel:"sfx"});
+ gbox.addAudio("powerpill", [audioserver+"powerup3.mp3",audioserver+"powerup3.ogg"],{channel:"sfx"});
+ gbox.addAudio("die", [audioserver+"die.mp3",audioserver+"die.ogg"],{channel:"sfx"});
+ gbox.addAudio("bonus", [audioserver+"coin.mp3",audioserver+"coin.ogg"],{channel:"sfx"});
+ gbox.addAudio("default-menu-option", [audioserver+"select.mp3",audioserver+"select.ogg"],{channel:"sfx"});
+ gbox.addAudio("default-menu-confirm", [audioserver+"start.mp3",audioserver+"start.ogg"],{channel:"sfx"});
+ gbox.addAudio("ingame", [audioserver+"capman-ingame.mp3",audioserver+"capman-ingame.ogg"],{channel:"bgmusic",loop:true});
+
+ // The loadAll function loads all the resources and triggers the main game loop
+ gbox.loadAll(go);
+
+ // Start the game
+ gbox.go();
+ });
+}, false);
+
+// Start game function
+var go = function() {
+ // console.log("--------------------------------------------------------- go");
+ // Set up the groups involved in the game, this is the different parts of the
+ // game that requires attention during the game loop, the order defines the drawing order of the groups
+ // background object will be draw first
+ // gbox.setGroups(["background","player","ghosts","bonus","sparks","gamecycle"]);
+ gbox.setGroups(["background", "player", "gamecycle"]);
+
+ // Set up the main loop object
+ maingame = gamecycle.createMaingame("gamecycle","gamecycle");
+
+ // Set method called each time we change the level
+ maingame.changeLevel = function(level) {
+ console.log("--------------------------------------------------------- changeLevel");
+
+ // Create a maze object based on the tilemap, mapping function
+ // and the function that determines if a tile is solid or not
+ maze = help.finalizeTilemap({
+ tileset: "maze",
+ map: help.asciiArtToMap(tilemap, tilemapTranslation),
+ tileIsSolid: isTileSolid
+ });
+
+ // Create a canvas using the calculated size of the maze
+ gbox.createCanvas("mazecanvas", {w:maze.w,h:maze.h});
+ // Render the tilemap to the canvas
+ gbox.blitTilemap(gbox.getCanvasContext("mazecanvas"), maze);
+
+ // // Set up the pill count
+ // this.pillscount = 0;
+ // for (var y=0;y7) this.pillscount++; // If the row/column contains a "pill" tile (8 for plain pill, 9 for powerpill), let's
+
+ this.newLife();
+ }
+
+ maingame.newLife = function() {
+ gbox.purgeGarbage(); // the gbox module have a garbage collector that runs sometime. Let's call this manually, for optimization (and better reinitialization)
+ toys.topview.spawn(gbox.getObject("player","capman"),{x:maze.hw-6,y:maze.hh+50,accx:0,accy:0,xpushing:false,ypushing:false}); // Our "capman" object into the "player" group spawns in the middle of the maze every time it spawns.
+ // maingame.addGhost({id:1,x:maze.hw-12,y:maze.hh-20}); // Ghost are added here
+ // maingame.addGhost({id:2,x:maze.hw-24,y:maze.hh-17});
+ // maingame.addGhost({id:3,x:maze.hw+4,y:maze.hh-20});
+ // maingame.addGhost({id:4,x:maze.hw+14,y:maze.hh-17});
+
+ // if (this.bonustimer) this.bonustimer=300;
+ // gbox.playAudio("ingame");
+
+ }
+
+ // This method is triggered once pr game
+ maingame.initializeGame = function() {
+ // console.log("--------------------------------------------------------- initializeGame");
+ // Set up the HUD used to signal all the different values visable to the user
+ maingame.hud.setWidget("label", {widget:"label", font:"small", value:"1UP", dx:240, dy:10, clear:true});
+ maingame.hud.setWidget("score",{widget:"label",font:"small",value:0,dx:240,dy:25,clear:true});
+ maingame.hud.setWidget("label",{widget:"label",font:"small",value:"HI",dx:240,dy:40,clear:true});
+ maingame.hud.setWidget("hiscore",{widget:"label",font:"small",value:0,dx:240,dy:55,clear:true});
+ maingame.hud.setWidget("lives",{widget:"symbols",minvalue:0,value:3-maingame.difficulty,maxshown:3,tileset:"capman",tiles:[5],dx:240,dy:70,gapx:16,gapy:0});
+ maingame.hud.setWidget("bonus",{widget:"stack",rightalign:true,tileset:"bonus",dx:gbox.getScreenW()-5,dy:gbox.getScreenH()-34,gapx:12,gapy:0,maxshown:8,value:[]});
+ maingame.hud.setWidget("stage",{widget:"label",font:"small",value:"",dx:0,dw:gbox.getScreenW()-5,dy:gbox.getScreenH()-13,halign:gbox.ALIGN_RIGHT,clear:true});
+ maingame.hud.setValue("hiscore","value",gbox.dataLoad("capman-hiscore"));
+
+ // Let's add the object that will draw the maze
+ gbox.addObject(drawMaze);
+ // Let's add the player
+ gbox.addObject(player);
+ }
+
+ maingame.gameMenu = function(reset) {
+ return true;
+ }
+
+ maingame.gameEvents = function() {
+ // console.log("--------------------------------------------------------- gameEvents");
+ }
+
+ // Show the intro screen
+ maingame.gameTitleIntroAnimation = function(reset) {
+ // console.log("--------------------------------------------------------- gameTitleIntroAnimation");
+
+ if(reset) {
+ toys.resetToy(this,"rising");
+ } else {
+ // Clear the screen
+ gbox.blitFade(gbox.getBufferContext(),{alpha:1});
+ // Show the logos
+ toys.logos.linear(this,"rising",{image:"logo",x:gbox.getScreenHW()-gbox.getImage("logo").hwidth,y:20,sx:gbox.getScreenHW()-gbox.getImage("logo").hwidth,sy:gbox.getScreenH(),speed:1,audioreach:"eatghost"});
+ }
+ };
+}
+
+//
+// Draws the player
+//
+var player = {
+ id:"capman",
+ group:"player",
+ tileset:"capman",
+ killed:false,
+ scorecombo:1,
+
+ initialize: function() {
+ // Sets up the objects, properties on our 2D map
+ toys.topview.initialize(this,{
+ colh:gbox.getTiles(this.tileset).tileh,
+ colw:gbox.getTiles(this.tileset).tilew,
+ staticspeed:2,
+ nodiagonals:true,
+ noreset:true,
+ frames:{
+ still:{ speed:2, frames:[0] },
+ hit:{speed:1,frames:[0,1,0,1]},
+ standup:{ speed:1, frames:[0] },
+ standdown:{ speed:1, frames:[0] },
+ standleft:{ speed:1, frames:[0] },
+ standright:{ speed:1, frames:[0] },
+ movingup:{speed:3,frames:[0,2,1,2] },
+ movingdown:{speed:3,frames:[0,4,3,4] },
+ movingleft:{speed:3,frames:[0,6,5,6] },
+ movingright:{speed:3,frames:[0,6,5,6] }
+ }
+ });
+ },
+
+ first: function() {
+ // Ensure we are showing the current correct frame out of 10 possible
+ this.counter = (this.counter+1) % 10;
+
+ // If capman is still alive and the game is not "hold" (level changing fadein/fadeouts etc.) and the "bullet timer" is not stopping the game.
+ if(!this.killed && !maingame.gameIsHold() && !maingame.bullettimer) {
+
+ // First of all, let's move.
+ // A little trick: capman cannot change direction, if hits a wall, so we backup capman's status here. Will restored if capman hits the wall.
+ var olddata = help.createModel(this,["x","y","accx","accy","xpushing","ypushing","facing"]);
+ // Set up the control keys for the player
+ toys.topview.controlKeys(this,{left:"left",right:"right",up:"up",down:"down"});
+ // Apply forces to the model
+ toys.topview.applyForces(this);
+
+ // Handle collisions with the map, accuracy and tolerance controls how precise the collision detection is
+ toys.topview.tileCollision(this, maze, "map", null, {tolerance:0,approximation:1});
+
+ // If we have a collision
+ if(this.touchedup||this.toucheddown||this.touchedleft||this.touchedright) {
+ help.copyModel(this,olddata);
+ toys.topview.applyForces(this);
+ toys.topview.tileCollision(this, maze, "map", null, {tolerance:0,approximation:1});
+ }
+
+ // The side warp. If capman reach one of the left or right side of the maze, is spawn on the other side,in the same direction
+ if ((this.x<0)&&(this.facing==toys.FACE_LEFT)) {
+ this.x = maze.w - this.w;
+ } else if ((this.x>(maze.w-this.w)) && (this.facing == toys.FACE_RIGHT)) {
+ this.x = 0;
+ }
+
+ // setFrame sets the right frame checking the facing and the defined animations in "initialize"
+ toys.topview.setFrame(this);
+
+ // // Grab the current tile in the map object
+ // var inmouth = help.getTileInMap(this.x+this.hw,this.y+this.hh, maze, 0); // I'll explain this the next line.
+ //
+ // // Handle pills
+ // if (inmouth>7) { // If capman is eating a pill (8 for normal pill, 9 for power pill)
+ // if (inmouth == 9) { // If is a powerpill
+ // gbox.hitAudio("powerpill"); // Play the powerpill sound. hitAudio plays an audio from start and is useful for sound effects. playAudio does nothing if the audio was already playing, so is useful for music playback.
+ // this.scorecombo=1; // Reset the combo counter.
+ // gbox.getObject("ghosts","ghost1").makeeatable(); // Make the ghosts vulnerable.
+ // gbox.getObject("ghosts","ghost2").makeeatable();
+ // gbox.getObject("ghosts","ghost3").makeeatable();
+ // gbox.getObject("ghosts","ghost4").makeeatable();
+ // } else
+ // gbox.hitAudio("eat"); // If is a classic pill, play the classic "gabogabo" sound!
+ // var mouthx=help.xPixelToTileX(maze,this.x+this.hw); // Let's get the pill coordinate in the maze...
+ // var mouthy=help.yPixelToTileY(maze,this.y+this.hh);
+ // help.setTileInMap(gbox.getCanvasContext("mazecanvas"),maze,mouthx,mouthy,null); // ... and set a null tile over that.
+ // maingame.hud.addValue("score","value",10); // Player earns 10 points. "hud" items also stores their values and can be used to store the real score.
+ // maingame.pillscount--; // Let's decrease the number of pills into the maze.
+ // }
+ }
+ },
+
+ blit:function() {
+ if (!this.killed) {
+ gbox.blitTile(gbox.getBufferContext(), {tileset:this.tileset, tile:this.frame, dx:this.x, dy:this.y, fliph:this.fliph, flipv:this.flipv, camera:this.camera, alpha:1});
+ }
+ },
+
+ // And now, a custom method. This one will kill the player and will be called by ghosts, when colliding with capman.
+ kill:function() {
+ // if (!this.killed) { // If we're alive...
+ // this.killed=true; // First of all, capman is killed. As you've seen, that makes capman invisible and on hold.
+ // gbox.hitAudio("die"); // Play the die sound
+ // maingame.hud.addValue("lives","value",-1); // Then decrease the lives count.
+ // maingame.playerDied({wait:50}); // Telling the main game cycle that the player died. The arguments sets a short delay after the last fadeout, for making visible the dead animation
+ // toys.generate.sparks.simple(this,"sparks",null,{tileset:this.tileset,frames:{speed:4,frames:[6,5,7,8,9,9,9,9]}});
+ // // And here comes a common trick: the player is still where was killed and a "spark" (i.e. unuseful animation) starts in the same place.
+ // // This method allows many nice tricks, since avoid destruction/recreation of the player object, allow a respawn the player in the place it was killed very easily (switching
+ // // the killed attribute. The "spark.simple" method spawns a spark in the same position of the object in the first argument.
+ // }
+ }
+}
+
+
+//
+// Draws the maze for the game
+//
+var drawMaze = {
+ id:"bg",
+ group:"background",
+
+ // This action is executed the first time the object is called, so...
+ initialize:function() {
+ // We place the camera a bit down, since the full maze doesn't fit the screen.
+ gbox.setCameraY(2, {w: maze.w, h: maze.h});
+ },
+
+ blit:function() {
+ // Clear the entire screen
+ gbox.blitFade(gbox.getBufferContext(),{alpha: 1});
+ gbox.blit(gbox.getBufferContext(), gbox.getCanvas("mazecanvas"),
+ {dx: 0,dy: 0,dw: gbox.getCanvas("mazecanvas").width, dh:gbox.getCanvas("mazecanvas").height, sourcecamera:true});
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+//
+// Map functions
+//
+// ----------------------------------------------------------------------------
+var isTileSolid = function(obj, t) {
+ return (t!==null)&&
+ ((t<6)||
+ ((t==6)&&(obj.status!="goout")&&(obj.status!="goin")));
+}
+
+// The tilemap used for the game
+var tilemap = [
+ "||T----------------------------------------------------TxxT----------------------------------------------------T||",
+ "|||| ||xx|| ||||",
+ "|||| . . . . . . . . . . . . ||xx|| . . . . . . . . . . . . ||||",
+ "|||| ||xx|| ||||",
+ "|||| . T------------T . T----------------T . ||xx|| . T----------------T . T------------T . ||||",
+ "|||| ||xxxxxxxxxx|| ||xxxxxxxxxxxxxx|| ||xx|| ||xxxxxxxxxxxxxx|| ||xxxxxxxxxx|| ||||",
+ "|||| o ||xxxxxxxxxx|| . ||xxxxxxxxxxxxxx|| . ||xx|| . ||xxxxxxxxxxxxxx|| . ||xxxxxxxxxx|| o ||||",
+ "|||| ||xxxxxxxxxx|| ||xxxxxxxxxxxxxx|| ||xx|| ||xxxxxxxxxxxxxx|| ||xxxxxxxxxx|| ||||",
+ "|||| . L------------J . L----------------J . L----J . L----------------J . L------------J . ||||",
+ "|||| ||||",
+ "|||| . . . . . . . . . . . . . . . . . . . . . . . . . . ||||",
+ "|||| ||||",
+ "|||| . T------------T . T----T . T----------------------------T . T----T . T------------T . ||||",
+ "|||| ||xxxxxxxxxx|| ||xx|| ||xxxxxxxxxxxxxxxxxxxxxxxxxx|| ||xx|| ||xxxxxxxxxx|| ||||",
+ "|||| . L------------J . ||xx|| . L------------TxxT------------J . ||xx|| . L------------J . ||||",
+ "|||| ||xx|| ||xx|| ||xx|| ||||",
+ "|||| . . . . . . ||xx|| . . . . ||xx|| . . . . ||xx|| . . . . . . ||||",
+ "|||| ||xx|| ||xx|| ||xx|| ||||",
+ "||L--------------------T . ||xxL------------T ||xx|| T------------Jxx|| . T--------------------J||",
+ "L--------------------T|| ||xxxxxxxxxxxxxx|| ||xx|| ||xxxxxxxxxxxxxx|| ||T--------------------J",
+ " |||| . ||xxT------------J L----J L------------Txx|| . |||| ",
+ " |||| ||xx|| ||xx|| |||| ",
+ " |||| . ||xx|| ||xx|| . |||| ",
+ " |||| ||xx|| ||xx|| |||| ",
+ " |||| . ||xx|| T---------~~~~~~~~~~---------T ||xx|| . |||| ",
+ "---------------------J|| ||xx|| || || ||xx|| ||L---------------------",
+ "-----------------------J . L----J || || L----J . L-----------------------",
+ " || || ",
+ " . || || . ",
+ " || || ",
+ "-----------------------T . T----T || || T----T . T-----------------------",
+ "---------------------T|| ||xx|| || || ||xx|| ||T---------------------",
+ " |||| . ||xx|| L----------------------------J ||xx|| . |||| ",
+ " |||| ||xx|| ||xx|| |||| ",
+ " |||| . ||xx|| ||xx|| . |||| ",
+ " |||| ||xx|| ||xx|| |||| ",
+ " |||| . ||xx|| T----------------------------T ||xx|| . |||| ",
+ "T--------------------J|| ||xx|| ||xxxxxxxxxxxxxxxxxxxxxxxxxx|| ||xx|| ||L--------------------T",
+ "||T--------------------J . L----J L------------TxxT------------J L----J . L--------------------T||",
+ "|||| ||xx|| ||||",
+ "|||| . . . . . . . . . . . . ||xx|| . . . . . . . . . . . . ||||",
+ "|||| ||xx|| ||||",
+ "|||| . T------------T . T----------------T . ||xx|| . T----------------T . T------------T . ||||",
+ "|||| ||xxxxxxxxxx|| ||xxxxxxxxxxxxxx|| ||xx|| ||xxxxxxxxxxxxxx|| ||xxxxxxxxxx|| ||||",
+ "|||| . L--------Txx|| . L----------------J . L----J . L----------------J . ||xxT--------J . ||||",
+ "|||| ||xx|| ||xx|| ||||",
+ "|||| o . . ||xx|| . . ||xx|| . . o ||||",
+ "|||| ||xx|| ||xx|| ||||",
+ "||L--------T . ||xx|| . T----T . T----------------------------T . T----T . ||xx|| . T--------J||",
+ "||xxxxxxxx|| ||xx|| ||xx|| ||xxxxxxxxxxxxxxxxxxxxxxxxxx|| ||xx|| ||xx|| ||xxxxxxxx||",
+ "||T--------J . L----J . ||xx|| . L------------TxxT------------J . ||xx|| . L----J . L--------T||",
+ "|||| ||xx|| ||xx|| ||xx|| ||||",
+ "|||| . . . . . . ||xx|| . . . . ||xx|| . . . . ||xx|| . . . . . . ||||",
+ "|||| ||xx|| ||xx|| ||xx|| ||||",
+ "|||| . T--------------------JxxL------------T . ||xx|| . T------------JxxL--------------------T . ||||",
+ "|||| ||xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|| ||xx|| ||xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|| ||||",
+ "|||| . L------------------------------------J . ||xx|| . L------------------------------------J . ||||",
+ "|||| ||xx|| ||||",
+ "|||| . . . . . . . . . . . . ||xx|| . . . . . . . . . . . . ||||",
+ "|||| ||xx|| ||||",
+ "||L----------------------------------------------------JxxL----------------------------------------------------J||",
+]
+
+// The tilemap translation array
+var tilemapTranslation = [[null," "],[0,"||"],[1,"--"],[2,"L-"],[3,"-J"],[4,"T-"],[5,"-T"],[6,"~~"],[7,"xx"],[8," ."],[9," o"]];
+
diff --git a/public/long.js b/public/long.js
new file mode 100644
index 0000000..3a472ac
--- /dev/null
+++ b/public/long.js
@@ -0,0 +1,854 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright 2009 Google Inc. All Rights Reserved
+
+/**
+ * Defines a Long class for representing a 64-bit two's-complement
+ * integer value, which faithfully simulates the behavior of a Java "Long". This
+ * implementation is derived from LongLib in GWT.
+ *
+ * Constructs a 64-bit two's-complement integer, given its low and high 32-bit
+ * values as *signed* integers. See the from* functions below for more
+ * convenient ways of constructing Longs.
+ *
+ * The internal representation of a Long is the two given signed, 32-bit values.
+ * We use 32-bit pieces because these are the size of integers on which
+ * Javascript performs bit-operations. For operations like addition and
+ * multiplication, we split each number into 16-bit pieces, which can easily be
+ * multiplied within Javascript's floating-point representation without overflow
+ * or change in sign.
+ *
+ * In the algorithms below, we frequently reduce the negative case to the
+ * positive case by negating the input(s) and then post-processing the result.
+ * Note that we must ALWAYS check specially whether those values are MIN_VALUE
+ * (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as
+ * a positive number, it overflows back into a negative). Not handling this
+ * case would often result in infinite recursion.
+ *
+ * @class Represents the BSON Long type.
+ * @param {Number} low the low (signed) 32 bits of the Long.
+ * @param {Number} high the high (signed) 32 bits of the Long.
+ */
+function Long(low, high) {
+ this._bsontype = 'Long';
+ /**
+ * @type {number}
+ * @api private
+ */
+ this.low_ = low | 0; // force into 32 signed bits.
+
+ /**
+ * @type {number}
+ * @api private
+ */
+ this.high_ = high | 0; // force into 32 signed bits.
+};
+
+/**
+ * Return the int value.
+ *
+ * @return {Number} the value, assuming it is a 32-bit integer.
+ * @api public
+ */
+Long.prototype.toInt = function() {
+ return this.low_;
+};
+
+/**
+ * Return the Number value.
+ *
+ * @return {Number} the closest floating-point representation to this value.
+ * @api public
+ */
+Long.prototype.toNumber = function() {
+ return this.high_ * Long.TWO_PWR_32_DBL_ +
+ this.getLowBitsUnsigned();
+};
+
+/**
+ * Return the JSON value.
+ *
+ * @return {String} the JSON representation.
+ * @api public
+ */
+Long.prototype.toJSON = function() {
+ return this.toString();
+}
+
+/**
+ * Return the String value.
+ *
+ * @param {Number} [opt_radix] the radix in which the text should be written.
+ * @return {String} the textual representation of this value.
+ * @api public
+ */
+Long.prototype.toString = function(opt_radix) {
+ var radix = opt_radix || 10;
+ if (radix < 2 || 36 < radix) {
+ throw Error('radix out of range: ' + radix);
+ }
+
+ if (this.isZero()) {
+ return '0';
+ }
+
+ if (this.isNegative()) {
+ if (this.equals(Long.MIN_VALUE)) {
+ // We need to change the Long value before it can be negated, so we remove
+ // the bottom-most digit in this base and then recurse to do the rest.
+ var radixLong = Long.fromNumber(radix);
+ var div = this.div(radixLong);
+ var rem = div.multiply(radixLong).subtract(this);
+ return div.toString(radix) + rem.toInt().toString(radix);
+ } else {
+ return '-' + this.negate().toString(radix);
+ }
+ }
+
+ // Do several (6) digits each time through the loop, so as to
+ // minimize the calls to the very expensive emulated div.
+ var radixToPower = Long.fromNumber(Math.pow(radix, 6));
+
+ var rem = this;
+ var result = '';
+ while (true) {
+ var remDiv = rem.div(radixToPower);
+ var intval = rem.subtract(remDiv.multiply(radixToPower)).toInt();
+ var digits = intval.toString(radix);
+
+ rem = remDiv;
+ if (rem.isZero()) {
+ return digits + result;
+ } else {
+ while (digits.length < 6) {
+ digits = '0' + digits;
+ }
+ result = '' + digits + result;
+ }
+ }
+};
+
+/**
+ * Return the high 32-bits value.
+ *
+ * @return {Number} the high 32-bits as a signed value.
+ * @api public
+ */
+Long.prototype.getHighBits = function() {
+ return this.high_;
+};
+
+/**
+ * Return the low 32-bits value.
+ *
+ * @return {Number} the low 32-bits as a signed value.
+ * @api public
+ */
+Long.prototype.getLowBits = function() {
+ return this.low_;
+};
+
+/**
+ * Return the low unsigned 32-bits value.
+ *
+ * @return {Number} the low 32-bits as an unsigned value.
+ * @api public
+ */
+Long.prototype.getLowBitsUnsigned = function() {
+ return (this.low_ >= 0) ?
+ this.low_ : Long.TWO_PWR_32_DBL_ + this.low_;
+};
+
+/**
+ * Returns the number of bits needed to represent the absolute value of this Long.
+ *
+ * @return {Number} Returns the number of bits needed to represent the absolute value of this Long.
+ * @api public
+ */
+Long.prototype.getNumBitsAbs = function() {
+ if (this.isNegative()) {
+ if (this.equals(Long.MIN_VALUE)) {
+ return 64;
+ } else {
+ return this.negate().getNumBitsAbs();
+ }
+ } else {
+ var val = this.high_ != 0 ? this.high_ : this.low_;
+ for (var bit = 31; bit > 0; bit--) {
+ if ((val & (1 << bit)) != 0) {
+ break;
+ }
+ }
+ return this.high_ != 0 ? bit + 33 : bit + 1;
+ }
+};
+
+/**
+ * Return whether this value is zero.
+ *
+ * @return {Boolean} whether this value is zero.
+ * @api public
+ */
+Long.prototype.isZero = function() {
+ return this.high_ == 0 && this.low_ == 0;
+};
+
+/**
+ * Return whether this value is negative.
+ *
+ * @return {Boolean} whether this value is negative.
+ * @api public
+ */
+Long.prototype.isNegative = function() {
+ return this.high_ < 0;
+};
+
+/**
+ * Return whether this value is odd.
+ *
+ * @return {Boolean} whether this value is odd.
+ * @api public
+ */
+Long.prototype.isOdd = function() {
+ return (this.low_ & 1) == 1;
+};
+
+/**
+ * Return whether this Long equals the other
+ *
+ * @param {Long} other Long to compare against.
+ * @return {Boolean} whether this Long equals the other
+ * @api public
+ */
+Long.prototype.equals = function(other) {
+ return (this.high_ == other.high_) && (this.low_ == other.low_);
+};
+
+/**
+ * Return whether this Long does not equal the other.
+ *
+ * @param {Long} other Long to compare against.
+ * @return {Boolean} whether this Long does not equal the other.
+ * @api public
+ */
+Long.prototype.notEquals = function(other) {
+ return (this.high_ != other.high_) || (this.low_ != other.low_);
+};
+
+/**
+ * Return whether this Long is less than the other.
+ *
+ * @param {Long} other Long to compare against.
+ * @return {Boolean} whether this Long is less than the other.
+ * @api public
+ */
+Long.prototype.lessThan = function(other) {
+ return this.compare(other) < 0;
+};
+
+/**
+ * Return whether this Long is less than or equal to the other.
+ *
+ * @param {Long} other Long to compare against.
+ * @return {Boolean} whether this Long is less than or equal to the other.
+ * @api public
+ */
+Long.prototype.lessThanOrEqual = function(other) {
+ return this.compare(other) <= 0;
+};
+
+/**
+ * Return whether this Long is greater than the other.
+ *
+ * @param {Long} other Long to compare against.
+ * @return {Boolean} whether this Long is greater than the other.
+ * @api public
+ */
+Long.prototype.greaterThan = function(other) {
+ return this.compare(other) > 0;
+};
+
+/**
+ * Return whether this Long is greater than or equal to the other.
+ *
+ * @param {Long} other Long to compare against.
+ * @return {Boolean} whether this Long is greater than or equal to the other.
+ * @api public
+ */
+Long.prototype.greaterThanOrEqual = function(other) {
+ return this.compare(other) >= 0;
+};
+
+/**
+ * Compares this Long with the given one.
+ *
+ * @param {Long} other Long to compare against.
+ * @return {Boolean} 0 if they are the same, 1 if the this is greater, and -1 if the given one is greater.
+ * @api public
+ */
+Long.prototype.compare = function(other) {
+ if (this.equals(other)) {
+ return 0;
+ }
+
+ var thisNeg = this.isNegative();
+ var otherNeg = other.isNegative();
+ if (thisNeg && !otherNeg) {
+ return -1;
+ }
+ if (!thisNeg && otherNeg) {
+ return 1;
+ }
+
+ // at this point, the signs are the same, so subtraction will not overflow
+ if (this.subtract(other).isNegative()) {
+ return -1;
+ } else {
+ return 1;
+ }
+};
+
+/**
+ * The negation of this value.
+ *
+ * @return {Long} the negation of this value.
+ * @api public
+ */
+Long.prototype.negate = function() {
+ if (this.equals(Long.MIN_VALUE)) {
+ return Long.MIN_VALUE;
+ } else {
+ return this.not().add(Long.ONE);
+ }
+};
+
+/**
+ * Returns the sum of this and the given Long.
+ *
+ * @param {Long} other Long to add to this one.
+ * @return {Long} the sum of this and the given Long.
+ * @api public
+ */
+Long.prototype.add = function(other) {
+ // Divide each number into 4 chunks of 16 bits, and then sum the chunks.
+
+ var a48 = this.high_ >>> 16;
+ var a32 = this.high_ & 0xFFFF;
+ var a16 = this.low_ >>> 16;
+ var a00 = this.low_ & 0xFFFF;
+
+ var b48 = other.high_ >>> 16;
+ var b32 = other.high_ & 0xFFFF;
+ var b16 = other.low_ >>> 16;
+ var b00 = other.low_ & 0xFFFF;
+
+ var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
+ c00 += a00 + b00;
+ c16 += c00 >>> 16;
+ c00 &= 0xFFFF;
+ c16 += a16 + b16;
+ c32 += c16 >>> 16;
+ c16 &= 0xFFFF;
+ c32 += a32 + b32;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c48 += a48 + b48;
+ c48 &= 0xFFFF;
+ return Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32);
+};
+
+/**
+ * Returns the difference of this and the given Long.
+ *
+ * @param {Long} other Long to subtract from this.
+ * @return {Long} the difference of this and the given Long.
+ * @api public
+ */
+Long.prototype.subtract = function(other) {
+ return this.add(other.negate());
+};
+
+/**
+ * Returns the product of this and the given Long.
+ *
+ * @param {Long} other Long to multiply with this.
+ * @return {Long} the product of this and the other.
+ * @api public
+ */
+Long.prototype.multiply = function(other) {
+ if (this.isZero()) {
+ return Long.ZERO;
+ } else if (other.isZero()) {
+ return Long.ZERO;
+ }
+
+ if (this.equals(Long.MIN_VALUE)) {
+ return other.isOdd() ? Long.MIN_VALUE : Long.ZERO;
+ } else if (other.equals(Long.MIN_VALUE)) {
+ return this.isOdd() ? Long.MIN_VALUE : Long.ZERO;
+ }
+
+ if (this.isNegative()) {
+ if (other.isNegative()) {
+ return this.negate().multiply(other.negate());
+ } else {
+ return this.negate().multiply(other).negate();
+ }
+ } else if (other.isNegative()) {
+ return this.multiply(other.negate()).negate();
+ }
+
+ // If both Longs are small, use float multiplication
+ if (this.lessThan(Long.TWO_PWR_24_) &&
+ other.lessThan(Long.TWO_PWR_24_)) {
+ return Long.fromNumber(this.toNumber() * other.toNumber());
+ }
+
+ // Divide each Long into 4 chunks of 16 bits, and then add up 4x4 products.
+ // We can skip products that would overflow.
+
+ var a48 = this.high_ >>> 16;
+ var a32 = this.high_ & 0xFFFF;
+ var a16 = this.low_ >>> 16;
+ var a00 = this.low_ & 0xFFFF;
+
+ var b48 = other.high_ >>> 16;
+ var b32 = other.high_ & 0xFFFF;
+ var b16 = other.low_ >>> 16;
+ var b00 = other.low_ & 0xFFFF;
+
+ var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
+ c00 += a00 * b00;
+ c16 += c00 >>> 16;
+ c00 &= 0xFFFF;
+ c16 += a16 * b00;
+ c32 += c16 >>> 16;
+ c16 &= 0xFFFF;
+ c16 += a00 * b16;
+ c32 += c16 >>> 16;
+ c16 &= 0xFFFF;
+ c32 += a32 * b00;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c32 += a16 * b16;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c32 += a00 * b32;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48;
+ c48 &= 0xFFFF;
+ return Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32);
+};
+
+/**
+ * Returns this Long divided by the given one.
+ *
+ * @param {Long} other Long by which to divide.
+ * @return {Long} this Long divided by the given one.
+ * @api public
+ */
+Long.prototype.div = function(other) {
+ if (other.isZero()) {
+ throw Error('division by zero');
+ } else if (this.isZero()) {
+ return Long.ZERO;
+ }
+
+ if (this.equals(Long.MIN_VALUE)) {
+ if (other.equals(Long.ONE) ||
+ other.equals(Long.NEG_ONE)) {
+ return Long.MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE
+ } else if (other.equals(Long.MIN_VALUE)) {
+ return Long.ONE;
+ } else {
+ // At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|.
+ var halfThis = this.shiftRight(1);
+ var approx = halfThis.div(other).shiftLeft(1);
+ if (approx.equals(Long.ZERO)) {
+ return other.isNegative() ? Long.ONE : Long.NEG_ONE;
+ } else {
+ var rem = this.subtract(other.multiply(approx));
+ var result = approx.add(rem.div(other));
+ return result;
+ }
+ }
+ } else if (other.equals(Long.MIN_VALUE)) {
+ return Long.ZERO;
+ }
+
+ if (this.isNegative()) {
+ if (other.isNegative()) {
+ return this.negate().div(other.negate());
+ } else {
+ return this.negate().div(other).negate();
+ }
+ } else if (other.isNegative()) {
+ return this.div(other.negate()).negate();
+ }
+
+ // Repeat the following until the remainder is less than other: find a
+ // floating-point that approximates remainder / other *from below*, add this
+ // into the result, and subtract it from the remainder. It is critical that
+ // the approximate value is less than or equal to the real value so that the
+ // remainder never becomes negative.
+ var res = Long.ZERO;
+ var rem = this;
+ while (rem.greaterThanOrEqual(other)) {
+ // Approximate the result of division. This may be a little greater or
+ // smaller than the actual value.
+ var approx = Math.max(1, Math.floor(rem.toNumber() / other.toNumber()));
+
+ // We will tweak the approximate result by changing it in the 48-th digit or
+ // the smallest non-fractional digit, whichever is larger.
+ var log2 = Math.ceil(Math.log(approx) / Math.LN2);
+ var delta = (log2 <= 48) ? 1 : Math.pow(2, log2 - 48);
+
+ // Decrease the approximation until it is smaller than the remainder. Note
+ // that if it is too large, the product overflows and is negative.
+ var approxRes = Long.fromNumber(approx);
+ var approxRem = approxRes.multiply(other);
+ while (approxRem.isNegative() || approxRem.greaterThan(rem)) {
+ approx -= delta;
+ approxRes = Long.fromNumber(approx);
+ approxRem = approxRes.multiply(other);
+ }
+
+ // We know the answer can't be zero... and actually, zero would cause
+ // infinite recursion since we would make no progress.
+ if (approxRes.isZero()) {
+ approxRes = Long.ONE;
+ }
+
+ res = res.add(approxRes);
+ rem = rem.subtract(approxRem);
+ }
+ return res;
+};
+
+/**
+ * Returns this Long modulo the given one.
+ *
+ * @param {Long} other Long by which to mod.
+ * @return {Long} this Long modulo the given one.
+ * @api public
+ */
+Long.prototype.modulo = function(other) {
+ return this.subtract(this.div(other).multiply(other));
+};
+
+/**
+ * The bitwise-NOT of this value.
+ *
+ * @return {Long} the bitwise-NOT of this value.
+ * @api public
+ */
+Long.prototype.not = function() {
+ return Long.fromBits(~this.low_, ~this.high_);
+};
+
+/**
+ * Returns the bitwise-AND of this Long and the given one.
+ *
+ * @param {Long} other the Long with which to AND.
+ * @return {Long} the bitwise-AND of this and the other.
+ * @api public
+ */
+Long.prototype.and = function(other) {
+ return Long.fromBits(this.low_ & other.low_, this.high_ & other.high_);
+};
+
+/**
+ * Returns the bitwise-OR of this Long and the given one.
+ *
+ * @param {Long} other the Long with which to OR.
+ * @return {Long} the bitwise-OR of this and the other.
+ * @api public
+ */
+Long.prototype.or = function(other) {
+ return Long.fromBits(this.low_ | other.low_, this.high_ | other.high_);
+};
+
+/**
+ * Returns the bitwise-XOR of this Long and the given one.
+ *
+ * @param {Long} other the Long with which to XOR.
+ * @return {Long} the bitwise-XOR of this and the other.
+ * @api public
+ */
+Long.prototype.xor = function(other) {
+ return Long.fromBits(this.low_ ^ other.low_, this.high_ ^ other.high_);
+};
+
+/**
+ * Returns this Long with bits shifted to the left by the given amount.
+ *
+ * @param {Number} numBits the number of bits by which to shift.
+ * @return {Long} this shifted to the left by the given amount.
+ * @api public
+ */
+Long.prototype.shiftLeft = function(numBits) {
+ numBits &= 63;
+ if (numBits == 0) {
+ return this;
+ } else {
+ var low = this.low_;
+ if (numBits < 32) {
+ var high = this.high_;
+ return Long.fromBits(
+ low << numBits,
+ (high << numBits) | (low >>> (32 - numBits)));
+ } else {
+ return Long.fromBits(0, low << (numBits - 32));
+ }
+ }
+};
+
+/**
+ * Returns this Long with bits shifted to the right by the given amount.
+ *
+ * @param {Number} numBits the number of bits by which to shift.
+ * @return {Long} this shifted to the right by the given amount.
+ * @api public
+ */
+Long.prototype.shiftRight = function(numBits) {
+ numBits &= 63;
+ if (numBits == 0) {
+ return this;
+ } else {
+ var high = this.high_;
+ if (numBits < 32) {
+ var low = this.low_;
+ return Long.fromBits(
+ (low >>> numBits) | (high << (32 - numBits)),
+ high >> numBits);
+ } else {
+ return Long.fromBits(
+ high >> (numBits - 32),
+ high >= 0 ? 0 : -1);
+ }
+ }
+};
+
+/**
+ * Returns this Long with bits shifted to the right by the given amount, with the new top bits matching the current sign bit.
+ *
+ * @param {Number} numBits the number of bits by which to shift.
+ * @return {Long} this shifted to the right by the given amount, with zeros placed into the new leading bits.
+ * @api public
+ */
+Long.prototype.shiftRightUnsigned = function(numBits) {
+ numBits &= 63;
+ if (numBits == 0) {
+ return this;
+ } else {
+ var high = this.high_;
+ if (numBits < 32) {
+ var low = this.low_;
+ return Long.fromBits(
+ (low >>> numBits) | (high << (32 - numBits)),
+ high >>> numBits);
+ } else if (numBits == 32) {
+ return Long.fromBits(high, 0);
+ } else {
+ return Long.fromBits(high >>> (numBits - 32), 0);
+ }
+ }
+};
+
+/**
+ * Returns a Long representing the given (32-bit) integer value.
+ *
+ * @param {Number} value the 32-bit integer in question.
+ * @return {Long} the corresponding Long value.
+ * @api public
+ */
+Long.fromInt = function(value) {
+ if (-128 <= value && value < 128) {
+ var cachedObj = Long.INT_CACHE_[value];
+ if (cachedObj) {
+ return cachedObj;
+ }
+ }
+
+ var obj = new Long(value | 0, value < 0 ? -1 : 0);
+ if (-128 <= value && value < 128) {
+ Long.INT_CACHE_[value] = obj;
+ }
+ return obj;
+};
+
+/**
+ * Returns a Long representing the given value, provided that it is a finite number. Otherwise, zero is returned.
+ *
+ * @param {Number} value the number in question.
+ * @return {Long} the corresponding Long value.
+ * @api public
+ */
+Long.fromNumber = function(value) {
+ if (isNaN(value) || !isFinite(value)) {
+ return Long.ZERO;
+ } else if (value <= -Long.TWO_PWR_63_DBL_) {
+ return Long.MIN_VALUE;
+ } else if (value + 1 >= Long.TWO_PWR_63_DBL_) {
+ return Long.MAX_VALUE;
+ } else if (value < 0) {
+ return Long.fromNumber(-value).negate();
+ } else {
+ return new Long(
+ (value % Long.TWO_PWR_32_DBL_) | 0,
+ (value / Long.TWO_PWR_32_DBL_) | 0);
+ }
+};
+
+/**
+ * Returns a Long representing the 64-bit integer that comes by concatenating the given high and low bits. Each is assumed to use 32 bits.
+ *
+ * @param {Number} lowBits the low 32-bits.
+ * @param {Number} highBits the high 32-bits.
+ * @return {Long} the corresponding Long value.
+ * @api public
+ */
+Long.fromBits = function(lowBits, highBits) {
+ return new Long(lowBits, highBits);
+};
+
+/**
+ * Returns a Long representation of the given string, written using the given radix.
+ *
+ * @param {String} str the textual representation of the Long.
+ * @param {Number} opt_radix the radix in which the text is written.
+ * @return {Long} the corresponding Long value.
+ * @api public
+ */
+Long.fromString = function(str, opt_radix) {
+ if (str.length == 0) {
+ throw Error('number format error: empty string');
+ }
+
+ var radix = opt_radix || 10;
+ if (radix < 2 || 36 < radix) {
+ throw Error('radix out of range: ' + radix);
+ }
+
+ if (str.charAt(0) == '-') {
+ return Long.fromString(str.substring(1), radix).negate();
+ } else if (str.indexOf('-') >= 0) {
+ throw Error('number format error: interior "-" character: ' + str);
+ }
+
+ // Do several (8) digits each time through the loop, so as to
+ // minimize the calls to the very expensive emulated div.
+ var radixToPower = Long.fromNumber(Math.pow(radix, 8));
+
+ var result = Long.ZERO;
+ for (var i = 0; i < str.length; i += 8) {
+ var size = Math.min(8, str.length - i);
+ var value = parseInt(str.substring(i, i + size), radix);
+ if (size < 8) {
+ var power = Long.fromNumber(Math.pow(radix, size));
+ result = result.multiply(power).add(Long.fromNumber(value));
+ } else {
+ result = result.multiply(radixToPower);
+ result = result.add(Long.fromNumber(value));
+ }
+ }
+ return result;
+};
+
+// NOTE: Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the
+// from* methods on which they depend.
+
+
+/**
+ * A cache of the Long representations of small integer values.
+ * @type {Object}
+ * @api private
+ */
+Long.INT_CACHE_ = {};
+
+// NOTE: the compiler should inline these constant values below and then remove
+// these variables, so there should be no runtime penalty for these.
+
+/**
+ * Number used repeated below in calculations. This must appear before the
+ * first call to any from* function below.
+ * @type {number}
+ * @api private
+ */
+Long.TWO_PWR_16_DBL_ = 1 << 16;
+
+/**
+ * @type {number}
+ * @api private
+ */
+Long.TWO_PWR_24_DBL_ = 1 << 24;
+
+/**
+ * @type {number}
+ * @api private
+ */
+Long.TWO_PWR_32_DBL_ = Long.TWO_PWR_16_DBL_ * Long.TWO_PWR_16_DBL_;
+
+/**
+ * @type {number}
+ * @api private
+ */
+Long.TWO_PWR_31_DBL_ = Long.TWO_PWR_32_DBL_ / 2;
+
+/**
+ * @type {number}
+ * @api private
+ */
+Long.TWO_PWR_48_DBL_ = Long.TWO_PWR_32_DBL_ * Long.TWO_PWR_16_DBL_;
+
+/**
+ * @type {number}
+ * @api private
+ */
+Long.TWO_PWR_64_DBL_ = Long.TWO_PWR_32_DBL_ * Long.TWO_PWR_32_DBL_;
+
+/**
+ * @type {number}
+ * @api private
+ */
+Long.TWO_PWR_63_DBL_ = Long.TWO_PWR_64_DBL_ / 2;
+
+/** @type {Long} */
+Long.ZERO = Long.fromInt(0);
+
+/** @type {Long} */
+Long.ONE = Long.fromInt(1);
+
+/** @type {Long} */
+Long.NEG_ONE = Long.fromInt(-1);
+
+/** @type {Long} */
+Long.MAX_VALUE =
+ Long.fromBits(0xFFFFFFFF | 0, 0x7FFFFFFF | 0);
+
+/** @type {Long} */
+Long.MIN_VALUE = Long.fromBits(0, 0x80000000 | 0);
+
+/**
+ * @type {Long}
+ * @api private
+ */
+Long.TWO_PWR_24_ = Long.fromInt(1 << 24);
+
+/**
+ * Expose.
+ */
+if(typeof window === 'undefined') {
+ exports.Long = Long;
+}
\ No newline at end of file
diff --git a/public/max_key.js b/public/max_key.js
new file mode 100644
index 0000000..98c6ee0
--- /dev/null
+++ b/public/max_key.js
@@ -0,0 +1,13 @@
+/**
+ * A class representation of the BSON MaxKey type.
+ *
+ * @class Represents the BSON MaxKey type.
+ * @return {MaxKey}
+ */
+function MaxKey() {
+ this._bsontype = 'MaxKey';
+}
+
+if(typeof window === 'undefined') {
+ exports.MaxKey = MaxKey;
+}
\ No newline at end of file
diff --git a/public/min_key.js b/public/min_key.js
new file mode 100644
index 0000000..3d4e7ae
--- /dev/null
+++ b/public/min_key.js
@@ -0,0 +1,13 @@
+/**
+ * A class representation of the BSON MinKey type.
+ *
+ * @class Represents the BSON MinKey type.
+ * @return {MinKey}
+ */
+function MinKey() {
+ this._bsontype = 'MinKey';
+}
+
+if(typeof window === 'undefined') {
+ exports.MinKey = MinKey;
+}
\ No newline at end of file
diff --git a/public/objectid.js b/public/objectid.js
new file mode 100644
index 0000000..f6a5b20
--- /dev/null
+++ b/public/objectid.js
@@ -0,0 +1,249 @@
+/**
+ * Module dependencies.
+ */
+if(typeof window === 'undefined') {
+ var BinaryParser = require('./binary_parser').BinaryParser;
+}
+
+/**
+ * Machine id.
+ *
+ * Create a random 3-byte value (i.e. unique for this
+ * process). Other drivers use a md5 of the machine id here, but
+ * that would mean an asyc call to gethostname, so we don't bother.
+ */
+var MACHINE_ID = parseInt(Math.random() * 0xFFFFFF, 10);
+
+// Regular expression that checks for hex value
+var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
+
+/**
+* Create a new ObjectID instance
+*
+* @class Represents the BSON ObjectID type
+* @param {String|Number} id Can be a 24 byte hex string, 12 byte binary string or a Number.
+* @return {Object} instance of ObjectID.
+*/
+function ObjectID(id) {
+ this._bsontype = 'ObjectID';
+
+ var self = this;
+ // Throw an error if it's not a valid setup
+ if(id != null && 'number' != typeof id && (id.length != 12 && id.length != 24)) throw new Error("Argument passed in must be a single String of 12 bytes or a string of 24 hex characters in hex format");
+ // Generate id based on the input
+ if(id == null || typeof id == 'number') {
+ this.id = this.generate(id);
+ } else if(id != null && id.length === 12) {
+ this.id = id;
+ } else if(checkForHexRegExp.test(id)) {
+ return ObjectID.createFromHexString(id);
+ } else {
+ this.id = id;
+ }
+
+ /**
+ * Returns the generation time in seconds that this ID was generated.
+ *
+ * @field generationTime
+ * @type {Number}
+ * @getter
+ * @setter
+ * @property return number of seconds in the timestamp part of the 12 byte id.
+ */
+ Object.defineProperty(this, "generationTime", {
+ enumerable: true
+ , get: function () {
+ return Math.floor(BinaryParser.decodeInt(this.id.substring(0,4), 32, true, true));
+ }
+ , set: function (value) {
+ var value = BinaryParser.encodeInt(value, 32, true, true);
+ this.id = value + this.id.substr(4);
+ }
+ });
+};
+
+/**
+* Update the ObjectID index used in generating new ObjectID's on the driver
+*
+* @return {Number} returns next index value.
+* @api private
+*/
+ObjectID.prototype.get_inc = function() {
+ return ObjectID.index = (ObjectID.index + 1) % 0xFFFFFF;
+};
+
+/**
+* Update the ObjectID index used in generating new ObjectID's on the driver
+*
+* @return {Number} returns next index value.
+* @api private
+*/
+ObjectID.prototype.getInc = function() {
+ return this.get_inc();
+};
+
+/**
+* Generate a 12 byte id string used in ObjectID's
+*
+* @param {Number} [time] optional parameter allowing to pass in a second based timestamp.
+* @return {String} return the 12 byte id binary string.
+* @api private
+*/
+ObjectID.prototype.generate = function(time) {
+ if ('number' == typeof time) {
+ var time4Bytes = BinaryParser.encodeInt(time, 32, true, true);
+ /* for time-based ObjectID the bytes following the time will be zeroed */
+ var machine3Bytes = BinaryParser.encodeInt(MACHINE_ID, 24, false);
+ var pid2Bytes = BinaryParser.fromShort(process.pid);
+ var index3Bytes = BinaryParser.encodeInt(this.get_inc(), 24, false, true);
+ } else {
+ var unixTime = parseInt(Date.now()/1000,10);
+ var time4Bytes = BinaryParser.encodeInt(unixTime, 32, true, true);
+ var machine3Bytes = BinaryParser.encodeInt(MACHINE_ID, 24, false);
+ var pid2Bytes = BinaryParser.fromShort(process.pid);
+ var index3Bytes = BinaryParser.encodeInt(this.get_inc(), 24, false, true);
+ }
+
+ return time4Bytes + machine3Bytes + pid2Bytes + index3Bytes;
+};
+
+/**
+* Return the ObjectID id as a 24 byte hex string representation
+*
+* @return {String} return the 24 byte hex string representation.
+* @api public
+*/
+ObjectID.prototype.toHexString = function() {
+ if(this.__id) return this.__id;
+
+ var hexString = ''
+ , number
+ , value;
+
+ for (var index = 0, len = this.id.length; index < len; index++) {
+ value = BinaryParser.toByte(this.id[index]);
+ number = value <= 15
+ ? '0' + value.toString(16)
+ : value.toString(16);
+ hexString = hexString + number;
+ }
+
+ return this.__id = hexString;
+};
+
+/**
+* Converts the id into a 24 byte hex string for printing
+*
+* @return {String} return the 24 byte hex string representation.
+* @api private
+*/
+ObjectID.prototype.toString = function() {
+ return this.toHexString();
+};
+
+/**
+* Converts to a string representation of this Id.
+*
+* @return {String} return the 24 byte hex string representation.
+* @api private
+*/
+ObjectID.prototype.inspect = ObjectID.prototype.toString;
+
+/**
+* Converts to its JSON representation.
+*
+* @return {String} return the 24 byte hex string representation.
+* @api private
+*/
+ObjectID.prototype.toJSON = function() {
+ return this.toHexString();
+};
+
+/**
+* Compares the equality of this ObjectID with `otherID`.
+*
+* @param {Object} otherID ObjectID instance to compare against.
+* @return {Bool} the result of comparing two ObjectID's
+* @api public
+*/
+ObjectID.prototype.equals = function equals (otherID) {
+ var id = (otherID instanceof ObjectID || otherID.toHexString)
+ ? otherID.id
+ : ObjectID.createFromHexString(otherID).id;
+
+ return this.id === id;
+}
+
+/**
+* Returns the generation time in seconds that this ID was generated.
+*
+* @return {Number} return number of seconds in the timestamp part of the 12 byte id.
+* @api public
+*/
+ObjectID.prototype.getTimestamp = function() {
+ var timestamp = new Date();
+ timestamp.setTime(Math.floor(BinaryParser.decodeInt(this.id.substring(0,4), 32, true, true)) * 1000);
+ return timestamp;
+}
+
+/**
+* @ignore
+* @api private
+*/
+ObjectID.index = 0;
+
+ObjectID.createPk = function createPk () {
+ return new ObjectID();
+};
+
+/**
+* Creates an ObjectID from a second based number, with the rest of the ObjectID zeroed out. Used for comparisons or sorting the ObjectID.
+*
+* @param {Number} time an integer number representing a number of seconds.
+* @return {ObjectID} return the created ObjectID
+* @api public
+*/
+ObjectID.createFromTime = function createFromHexString(time) {
+ var time4Bytes = BinaryParser.encodeInt(time, 32, true, true);
+ var objectID = new ObjectID();
+ objectID.id = BinaryParser.encodeInt(time, 32, true, true) + BinaryParser.encodeInt(0, 64, true, true)
+ return objectID;
+};
+
+/**
+* Creates an ObjectID from a hex string representation of an ObjectID.
+*
+* @param {String} hexString create a ObjectID from a passed in 24 byte hexstring.
+* @return {ObjectID} return the created ObjectID
+* @api public
+*/
+ObjectID.createFromHexString = function createFromHexString (hexString) {
+ // Throw an error if it's not a valid setup
+ if(hexString != null && hexString.length != 24) throw new Error("Argument passed in must be a single String of 12 bytes or a string of 24 hex characters in hex format");
+
+ var len = hexString.length;
+
+ if(len > 12*2) {
+ throw new Error('Id cannot be longer than 12 bytes');
+ }
+
+ var result = ''
+ , string
+ , number;
+
+ for (var index = 0; index < len; index += 2) {
+ string = hexString.substr(index, 2);
+ number = parseInt(string, 16);
+ result += BinaryParser.fromByte(number);
+ }
+
+ return new ObjectID(result);
+};
+
+/**
+ * Expose.
+ */
+if(typeof window === 'undefined') {
+ exports.ObjectID = ObjectID;
+}
+
diff --git a/public/resources/audio/capman-ingame.mp3 b/public/resources/audio/capman-ingame.mp3
new file mode 100644
index 0000000..0b1d858
Binary files /dev/null and b/public/resources/audio/capman-ingame.mp3 differ
diff --git a/public/resources/audio/capman-ingame.ogg b/public/resources/audio/capman-ingame.ogg
new file mode 100644
index 0000000..2c35939
Binary files /dev/null and b/public/resources/audio/capman-ingame.ogg differ
diff --git a/public/resources/audio/coin.mp3 b/public/resources/audio/coin.mp3
new file mode 100644
index 0000000..eda4dfd
Binary files /dev/null and b/public/resources/audio/coin.mp3 differ
diff --git a/public/resources/audio/coin.ogg b/public/resources/audio/coin.ogg
new file mode 100644
index 0000000..5cfb9c0
Binary files /dev/null and b/public/resources/audio/coin.ogg differ
diff --git a/public/resources/audio/die.mp3 b/public/resources/audio/die.mp3
new file mode 100644
index 0000000..9d8d0e5
Binary files /dev/null and b/public/resources/audio/die.mp3 differ
diff --git a/public/resources/audio/die.ogg b/public/resources/audio/die.ogg
new file mode 100644
index 0000000..da474bd
Binary files /dev/null and b/public/resources/audio/die.ogg differ
diff --git a/public/resources/audio/eat.mp3 b/public/resources/audio/eat.mp3
new file mode 100644
index 0000000..da3703d
Binary files /dev/null and b/public/resources/audio/eat.mp3 differ
diff --git a/public/resources/audio/eat.ogg b/public/resources/audio/eat.ogg
new file mode 100644
index 0000000..62c25bf
Binary files /dev/null and b/public/resources/audio/eat.ogg differ
diff --git a/public/resources/audio/halloffame-theme.mp3 b/public/resources/audio/halloffame-theme.mp3
new file mode 100644
index 0000000..c35c26b
Binary files /dev/null and b/public/resources/audio/halloffame-theme.mp3 differ
diff --git a/public/resources/audio/halloffame-theme.ogg b/public/resources/audio/halloffame-theme.ogg
new file mode 100644
index 0000000..70acf8d
Binary files /dev/null and b/public/resources/audio/halloffame-theme.ogg differ
diff --git a/public/resources/audio/laser.mp3 b/public/resources/audio/laser.mp3
new file mode 100644
index 0000000..6bd69a4
Binary files /dev/null and b/public/resources/audio/laser.mp3 differ
diff --git a/public/resources/audio/laser.ogg b/public/resources/audio/laser.ogg
new file mode 100644
index 0000000..2a54b13
Binary files /dev/null and b/public/resources/audio/laser.ogg differ
diff --git a/public/resources/audio/powerup3.mp3 b/public/resources/audio/powerup3.mp3
new file mode 100644
index 0000000..18b8d23
Binary files /dev/null and b/public/resources/audio/powerup3.mp3 differ
diff --git a/public/resources/audio/powerup3.ogg b/public/resources/audio/powerup3.ogg
new file mode 100644
index 0000000..9d2a471
Binary files /dev/null and b/public/resources/audio/powerup3.ogg differ
diff --git a/public/resources/audio/select.mp3 b/public/resources/audio/select.mp3
new file mode 100644
index 0000000..fd5ceec
Binary files /dev/null and b/public/resources/audio/select.mp3 differ
diff --git a/public/resources/audio/select.ogg b/public/resources/audio/select.ogg
new file mode 100644
index 0000000..0829a93
Binary files /dev/null and b/public/resources/audio/select.ogg differ
diff --git a/public/resources/audio/start.mp3 b/public/resources/audio/start.mp3
new file mode 100644
index 0000000..01de403
Binary files /dev/null and b/public/resources/audio/start.mp3 differ
diff --git a/public/resources/audio/start.ogg b/public/resources/audio/start.ogg
new file mode 100644
index 0000000..a6bccd4
Binary files /dev/null and b/public/resources/audio/start.ogg differ
diff --git a/public/resources/capman/cels.png b/public/resources/capman/cels.png
new file mode 100644
index 0000000..cd76f6d
Binary files /dev/null and b/public/resources/capman/cels.png differ
diff --git a/public/resources/capman/font.png b/public/resources/capman/font.png
new file mode 100644
index 0000000..89d9c09
Binary files /dev/null and b/public/resources/capman/font.png differ
diff --git a/public/resources/capman/logo.png b/public/resources/capman/logo.png
new file mode 100644
index 0000000..17885d9
Binary files /dev/null and b/public/resources/capman/logo.png differ
diff --git a/public/resources/halloffame/font.png b/public/resources/halloffame/font.png
new file mode 100644
index 0000000..301219a
Binary files /dev/null and b/public/resources/halloffame/font.png differ
diff --git a/public/resources/halloffame/kesiev.png b/public/resources/halloffame/kesiev.png
new file mode 100644
index 0000000..bbcc12d
Binary files /dev/null and b/public/resources/halloffame/kesiev.png differ
diff --git a/public/resources/halloffame/logo.png b/public/resources/halloffame/logo.png
new file mode 100644
index 0000000..d80063f
Binary files /dev/null and b/public/resources/halloffame/logo.png differ
diff --git a/public/symbol.js b/public/symbol.js
new file mode 100644
index 0000000..22d57d9
--- /dev/null
+++ b/public/symbol.js
@@ -0,0 +1,49 @@
+/**
+ * A class representation of the BSON Symbol type.
+ *
+ * @class Represents the BSON Symbol type.
+ * @param {String} value the string representing the symbol.
+ * @return {Symbol}
+ */
+function Symbol(value) {
+ this._bsontype = 'Symbol';
+ this.value = value;
+}
+
+/**
+ * Access the wrapped string value.
+ *
+ * @return {String} returns the wrapped string.
+ * @api public
+ */
+Symbol.prototype.valueOf = function() {
+ return this.value;
+};
+
+/**
+ * @ignore
+ * @api private
+ */
+Symbol.prototype.toString = function() {
+ return this.value;
+}
+
+/**
+ * @ignore
+ * @api private
+ */
+Symbol.prototype.inspect = function() {
+ return this.value;
+}
+
+/**
+ * @ignore
+ * @api private
+ */
+Symbol.prototype.toJSON = function() {
+ return this.value;
+}
+
+if(typeof window === 'undefined') {
+ exports.Symbol = Symbol;
+}
\ No newline at end of file
diff --git a/public/timestamp.js b/public/timestamp.js
new file mode 100644
index 0000000..f417892
--- /dev/null
+++ b/public/timestamp.js
@@ -0,0 +1,854 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright 2009 Google Inc. All Rights Reserved
+
+/**
+ * Defines a Timestamp class for representing a 64-bit two's-complement
+ * integer value, which faithfully simulates the behavior of a Java "Timestamp". This
+ * implementation is derived from TimestampLib in GWT.
+ *
+ * Constructs a 64-bit two's-complement integer, given its low and high 32-bit
+ * values as *signed* integers. See the from* functions below for more
+ * convenient ways of constructing Timestamps.
+ *
+ * The internal representation of a Timestamp is the two given signed, 32-bit values.
+ * We use 32-bit pieces because these are the size of integers on which
+ * Javascript performs bit-operations. For operations like addition and
+ * multiplication, we split each number into 16-bit pieces, which can easily be
+ * multiplied within Javascript's floating-point representation without overflow
+ * or change in sign.
+ *
+ * In the algorithms below, we frequently reduce the negative case to the
+ * positive case by negating the input(s) and then post-processing the result.
+ * Note that we must ALWAYS check specially whether those values are MIN_VALUE
+ * (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as
+ * a positive number, it overflows back into a negative). Not handling this
+ * case would often result in infinite recursion.
+ *
+ * @class Represents the BSON Timestamp type.
+ * @param {Number} low the low (signed) 32 bits of the Timestamp.
+ * @param {Number} high the high (signed) 32 bits of the Timestamp.
+ */
+function Timestamp(low, high) {
+ this._bsontype = 'Timestamp';
+ /**
+ * @type {number}
+ * @api private
+ */
+ this.low_ = low | 0; // force into 32 signed bits.
+
+ /**
+ * @type {number}
+ * @api private
+ */
+ this.high_ = high | 0; // force into 32 signed bits.
+};
+
+/**
+ * Return the int value.
+ *
+ * @return {Number} the value, assuming it is a 32-bit integer.
+ * @api public
+ */
+Timestamp.prototype.toInt = function() {
+ return this.low_;
+};
+
+/**
+ * Return the Number value.
+ *
+ * @return {Number} the closest floating-point representation to this value.
+ * @api public
+ */
+Timestamp.prototype.toNumber = function() {
+ return this.high_ * Timestamp.TWO_PWR_32_DBL_ +
+ this.getLowBitsUnsigned();
+};
+
+/**
+ * Return the JSON value.
+ *
+ * @return {String} the JSON representation.
+ * @api public
+ */
+Timestamp.prototype.toJSON = function() {
+ return this.toString();
+}
+
+/**
+ * Return the String value.
+ *
+ * @param {Number} [opt_radix] the radix in which the text should be written.
+ * @return {String} the textual representation of this value.
+ * @api public
+ */
+Timestamp.prototype.toString = function(opt_radix) {
+ var radix = opt_radix || 10;
+ if (radix < 2 || 36 < radix) {
+ throw Error('radix out of range: ' + radix);
+ }
+
+ if (this.isZero()) {
+ return '0';
+ }
+
+ if (this.isNegative()) {
+ if (this.equals(Timestamp.MIN_VALUE)) {
+ // We need to change the Timestamp value before it can be negated, so we remove
+ // the bottom-most digit in this base and then recurse to do the rest.
+ var radixTimestamp = Timestamp.fromNumber(radix);
+ var div = this.div(radixTimestamp);
+ var rem = div.multiply(radixTimestamp).subtract(this);
+ return div.toString(radix) + rem.toInt().toString(radix);
+ } else {
+ return '-' + this.negate().toString(radix);
+ }
+ }
+
+ // Do several (6) digits each time through the loop, so as to
+ // minimize the calls to the very expensive emulated div.
+ var radixToPower = Timestamp.fromNumber(Math.pow(radix, 6));
+
+ var rem = this;
+ var result = '';
+ while (true) {
+ var remDiv = rem.div(radixToPower);
+ var intval = rem.subtract(remDiv.multiply(radixToPower)).toInt();
+ var digits = intval.toString(radix);
+
+ rem = remDiv;
+ if (rem.isZero()) {
+ return digits + result;
+ } else {
+ while (digits.length < 6) {
+ digits = '0' + digits;
+ }
+ result = '' + digits + result;
+ }
+ }
+};
+
+/**
+ * Return the high 32-bits value.
+ *
+ * @return {Number} the high 32-bits as a signed value.
+ * @api public
+ */
+Timestamp.prototype.getHighBits = function() {
+ return this.high_;
+};
+
+/**
+ * Return the low 32-bits value.
+ *
+ * @return {Number} the low 32-bits as a signed value.
+ * @api public
+ */
+Timestamp.prototype.getLowBits = function() {
+ return this.low_;
+};
+
+/**
+ * Return the low unsigned 32-bits value.
+ *
+ * @return {Number} the low 32-bits as an unsigned value.
+ * @api public
+ */
+Timestamp.prototype.getLowBitsUnsigned = function() {
+ return (this.low_ >= 0) ?
+ this.low_ : Timestamp.TWO_PWR_32_DBL_ + this.low_;
+};
+
+/**
+ * Returns the number of bits needed to represent the absolute value of this Timestamp.
+ *
+ * @return {Number} Returns the number of bits needed to represent the absolute value of this Timestamp.
+ * @api public
+ */
+Timestamp.prototype.getNumBitsAbs = function() {
+ if (this.isNegative()) {
+ if (this.equals(Timestamp.MIN_VALUE)) {
+ return 64;
+ } else {
+ return this.negate().getNumBitsAbs();
+ }
+ } else {
+ var val = this.high_ != 0 ? this.high_ : this.low_;
+ for (var bit = 31; bit > 0; bit--) {
+ if ((val & (1 << bit)) != 0) {
+ break;
+ }
+ }
+ return this.high_ != 0 ? bit + 33 : bit + 1;
+ }
+};
+
+/**
+ * Return whether this value is zero.
+ *
+ * @return {Boolean} whether this value is zero.
+ * @api public
+ */
+Timestamp.prototype.isZero = function() {
+ return this.high_ == 0 && this.low_ == 0;
+};
+
+/**
+ * Return whether this value is negative.
+ *
+ * @return {Boolean} whether this value is negative.
+ * @api public
+ */
+Timestamp.prototype.isNegative = function() {
+ return this.high_ < 0;
+};
+
+/**
+ * Return whether this value is odd.
+ *
+ * @return {Boolean} whether this value is odd.
+ * @api public
+ */
+Timestamp.prototype.isOdd = function() {
+ return (this.low_ & 1) == 1;
+};
+
+/**
+ * Return whether this Timestamp equals the other
+ *
+ * @param {Timestamp} other Timestamp to compare against.
+ * @return {Boolean} whether this Timestamp equals the other
+ * @api public
+ */
+Timestamp.prototype.equals = function(other) {
+ return (this.high_ == other.high_) && (this.low_ == other.low_);
+};
+
+/**
+ * Return whether this Timestamp does not equal the other.
+ *
+ * @param {Timestamp} other Timestamp to compare against.
+ * @return {Boolean} whether this Timestamp does not equal the other.
+ * @api public
+ */
+Timestamp.prototype.notEquals = function(other) {
+ return (this.high_ != other.high_) || (this.low_ != other.low_);
+};
+
+/**
+ * Return whether this Timestamp is less than the other.
+ *
+ * @param {Timestamp} other Timestamp to compare against.
+ * @return {Boolean} whether this Timestamp is less than the other.
+ * @api public
+ */
+Timestamp.prototype.lessThan = function(other) {
+ return this.compare(other) < 0;
+};
+
+/**
+ * Return whether this Timestamp is less than or equal to the other.
+ *
+ * @param {Timestamp} other Timestamp to compare against.
+ * @return {Boolean} whether this Timestamp is less than or equal to the other.
+ * @api public
+ */
+Timestamp.prototype.lessThanOrEqual = function(other) {
+ return this.compare(other) <= 0;
+};
+
+/**
+ * Return whether this Timestamp is greater than the other.
+ *
+ * @param {Timestamp} other Timestamp to compare against.
+ * @return {Boolean} whether this Timestamp is greater than the other.
+ * @api public
+ */
+Timestamp.prototype.greaterThan = function(other) {
+ return this.compare(other) > 0;
+};
+
+/**
+ * Return whether this Timestamp is greater than or equal to the other.
+ *
+ * @param {Timestamp} other Timestamp to compare against.
+ * @return {Boolean} whether this Timestamp is greater than or equal to the other.
+ * @api public
+ */
+Timestamp.prototype.greaterThanOrEqual = function(other) {
+ return this.compare(other) >= 0;
+};
+
+/**
+ * Compares this Timestamp with the given one.
+ *
+ * @param {Timestamp} other Timestamp to compare against.
+ * @return {Boolean} 0 if they are the same, 1 if the this is greater, and -1 if the given one is greater.
+ * @api public
+ */
+Timestamp.prototype.compare = function(other) {
+ if (this.equals(other)) {
+ return 0;
+ }
+
+ var thisNeg = this.isNegative();
+ var otherNeg = other.isNegative();
+ if (thisNeg && !otherNeg) {
+ return -1;
+ }
+ if (!thisNeg && otherNeg) {
+ return 1;
+ }
+
+ // at this point, the signs are the same, so subtraction will not overflow
+ if (this.subtract(other).isNegative()) {
+ return -1;
+ } else {
+ return 1;
+ }
+};
+
+/**
+ * The negation of this value.
+ *
+ * @return {Timestamp} the negation of this value.
+ * @api public
+ */
+Timestamp.prototype.negate = function() {
+ if (this.equals(Timestamp.MIN_VALUE)) {
+ return Timestamp.MIN_VALUE;
+ } else {
+ return this.not().add(Timestamp.ONE);
+ }
+};
+
+/**
+ * Returns the sum of this and the given Timestamp.
+ *
+ * @param {Timestamp} other Timestamp to add to this one.
+ * @return {Timestamp} the sum of this and the given Timestamp.
+ * @api public
+ */
+Timestamp.prototype.add = function(other) {
+ // Divide each number into 4 chunks of 16 bits, and then sum the chunks.
+
+ var a48 = this.high_ >>> 16;
+ var a32 = this.high_ & 0xFFFF;
+ var a16 = this.low_ >>> 16;
+ var a00 = this.low_ & 0xFFFF;
+
+ var b48 = other.high_ >>> 16;
+ var b32 = other.high_ & 0xFFFF;
+ var b16 = other.low_ >>> 16;
+ var b00 = other.low_ & 0xFFFF;
+
+ var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
+ c00 += a00 + b00;
+ c16 += c00 >>> 16;
+ c00 &= 0xFFFF;
+ c16 += a16 + b16;
+ c32 += c16 >>> 16;
+ c16 &= 0xFFFF;
+ c32 += a32 + b32;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c48 += a48 + b48;
+ c48 &= 0xFFFF;
+ return Timestamp.fromBits((c16 << 16) | c00, (c48 << 16) | c32);
+};
+
+/**
+ * Returns the difference of this and the given Timestamp.
+ *
+ * @param {Timestamp} other Timestamp to subtract from this.
+ * @return {Timestamp} the difference of this and the given Timestamp.
+ * @api public
+ */
+Timestamp.prototype.subtract = function(other) {
+ return this.add(other.negate());
+};
+
+/**
+ * Returns the product of this and the given Timestamp.
+ *
+ * @param {Timestamp} other Timestamp to multiply with this.
+ * @return {Timestamp} the product of this and the other.
+ * @api public
+ */
+Timestamp.prototype.multiply = function(other) {
+ if (this.isZero()) {
+ return Timestamp.ZERO;
+ } else if (other.isZero()) {
+ return Timestamp.ZERO;
+ }
+
+ if (this.equals(Timestamp.MIN_VALUE)) {
+ return other.isOdd() ? Timestamp.MIN_VALUE : Timestamp.ZERO;
+ } else if (other.equals(Timestamp.MIN_VALUE)) {
+ return this.isOdd() ? Timestamp.MIN_VALUE : Timestamp.ZERO;
+ }
+
+ if (this.isNegative()) {
+ if (other.isNegative()) {
+ return this.negate().multiply(other.negate());
+ } else {
+ return this.negate().multiply(other).negate();
+ }
+ } else if (other.isNegative()) {
+ return this.multiply(other.negate()).negate();
+ }
+
+ // If both Timestamps are small, use float multiplication
+ if (this.lessThan(Timestamp.TWO_PWR_24_) &&
+ other.lessThan(Timestamp.TWO_PWR_24_)) {
+ return Timestamp.fromNumber(this.toNumber() * other.toNumber());
+ }
+
+ // Divide each Timestamp into 4 chunks of 16 bits, and then add up 4x4 products.
+ // We can skip products that would overflow.
+
+ var a48 = this.high_ >>> 16;
+ var a32 = this.high_ & 0xFFFF;
+ var a16 = this.low_ >>> 16;
+ var a00 = this.low_ & 0xFFFF;
+
+ var b48 = other.high_ >>> 16;
+ var b32 = other.high_ & 0xFFFF;
+ var b16 = other.low_ >>> 16;
+ var b00 = other.low_ & 0xFFFF;
+
+ var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
+ c00 += a00 * b00;
+ c16 += c00 >>> 16;
+ c00 &= 0xFFFF;
+ c16 += a16 * b00;
+ c32 += c16 >>> 16;
+ c16 &= 0xFFFF;
+ c16 += a00 * b16;
+ c32 += c16 >>> 16;
+ c16 &= 0xFFFF;
+ c32 += a32 * b00;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c32 += a16 * b16;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c32 += a00 * b32;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48;
+ c48 &= 0xFFFF;
+ return Timestamp.fromBits((c16 << 16) | c00, (c48 << 16) | c32);
+};
+
+/**
+ * Returns this Timestamp divided by the given one.
+ *
+ * @param {Timestamp} other Timestamp by which to divide.
+ * @return {Timestamp} this Timestamp divided by the given one.
+ * @api public
+ */
+Timestamp.prototype.div = function(other) {
+ if (other.isZero()) {
+ throw Error('division by zero');
+ } else if (this.isZero()) {
+ return Timestamp.ZERO;
+ }
+
+ if (this.equals(Timestamp.MIN_VALUE)) {
+ if (other.equals(Timestamp.ONE) ||
+ other.equals(Timestamp.NEG_ONE)) {
+ return Timestamp.MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE
+ } else if (other.equals(Timestamp.MIN_VALUE)) {
+ return Timestamp.ONE;
+ } else {
+ // At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|.
+ var halfThis = this.shiftRight(1);
+ var approx = halfThis.div(other).shiftLeft(1);
+ if (approx.equals(Timestamp.ZERO)) {
+ return other.isNegative() ? Timestamp.ONE : Timestamp.NEG_ONE;
+ } else {
+ var rem = this.subtract(other.multiply(approx));
+ var result = approx.add(rem.div(other));
+ return result;
+ }
+ }
+ } else if (other.equals(Timestamp.MIN_VALUE)) {
+ return Timestamp.ZERO;
+ }
+
+ if (this.isNegative()) {
+ if (other.isNegative()) {
+ return this.negate().div(other.negate());
+ } else {
+ return this.negate().div(other).negate();
+ }
+ } else if (other.isNegative()) {
+ return this.div(other.negate()).negate();
+ }
+
+ // Repeat the following until the remainder is less than other: find a
+ // floating-point that approximates remainder / other *from below*, add this
+ // into the result, and subtract it from the remainder. It is critical that
+ // the approximate value is less than or equal to the real value so that the
+ // remainder never becomes negative.
+ var res = Timestamp.ZERO;
+ var rem = this;
+ while (rem.greaterThanOrEqual(other)) {
+ // Approximate the result of division. This may be a little greater or
+ // smaller than the actual value.
+ var approx = Math.max(1, Math.floor(rem.toNumber() / other.toNumber()));
+
+ // We will tweak the approximate result by changing it in the 48-th digit or
+ // the smallest non-fractional digit, whichever is larger.
+ var log2 = Math.ceil(Math.log(approx) / Math.LN2);
+ var delta = (log2 <= 48) ? 1 : Math.pow(2, log2 - 48);
+
+ // Decrease the approximation until it is smaller than the remainder. Note
+ // that if it is too large, the product overflows and is negative.
+ var approxRes = Timestamp.fromNumber(approx);
+ var approxRem = approxRes.multiply(other);
+ while (approxRem.isNegative() || approxRem.greaterThan(rem)) {
+ approx -= delta;
+ approxRes = Timestamp.fromNumber(approx);
+ approxRem = approxRes.multiply(other);
+ }
+
+ // We know the answer can't be zero... and actually, zero would cause
+ // infinite recursion since we would make no progress.
+ if (approxRes.isZero()) {
+ approxRes = Timestamp.ONE;
+ }
+
+ res = res.add(approxRes);
+ rem = rem.subtract(approxRem);
+ }
+ return res;
+};
+
+/**
+ * Returns this Timestamp modulo the given one.
+ *
+ * @param {Timestamp} other Timestamp by which to mod.
+ * @return {Timestamp} this Timestamp modulo the given one.
+ * @api public
+ */
+Timestamp.prototype.modulo = function(other) {
+ return this.subtract(this.div(other).multiply(other));
+};
+
+/**
+ * The bitwise-NOT of this value.
+ *
+ * @return {Timestamp} the bitwise-NOT of this value.
+ * @api public
+ */
+Timestamp.prototype.not = function() {
+ return Timestamp.fromBits(~this.low_, ~this.high_);
+};
+
+/**
+ * Returns the bitwise-AND of this Timestamp and the given one.
+ *
+ * @param {Timestamp} other the Timestamp with which to AND.
+ * @return {Timestamp} the bitwise-AND of this and the other.
+ * @api public
+ */
+Timestamp.prototype.and = function(other) {
+ return Timestamp.fromBits(this.low_ & other.low_, this.high_ & other.high_);
+};
+
+/**
+ * Returns the bitwise-OR of this Timestamp and the given one.
+ *
+ * @param {Timestamp} other the Timestamp with which to OR.
+ * @return {Timestamp} the bitwise-OR of this and the other.
+ * @api public
+ */
+Timestamp.prototype.or = function(other) {
+ return Timestamp.fromBits(this.low_ | other.low_, this.high_ | other.high_);
+};
+
+/**
+ * Returns the bitwise-XOR of this Timestamp and the given one.
+ *
+ * @param {Timestamp} other the Timestamp with which to XOR.
+ * @return {Timestamp} the bitwise-XOR of this and the other.
+ * @api public
+ */
+Timestamp.prototype.xor = function(other) {
+ return Timestamp.fromBits(this.low_ ^ other.low_, this.high_ ^ other.high_);
+};
+
+/**
+ * Returns this Timestamp with bits shifted to the left by the given amount.
+ *
+ * @param {Number} numBits the number of bits by which to shift.
+ * @return {Timestamp} this shifted to the left by the given amount.
+ * @api public
+ */
+Timestamp.prototype.shiftLeft = function(numBits) {
+ numBits &= 63;
+ if (numBits == 0) {
+ return this;
+ } else {
+ var low = this.low_;
+ if (numBits < 32) {
+ var high = this.high_;
+ return Timestamp.fromBits(
+ low << numBits,
+ (high << numBits) | (low >>> (32 - numBits)));
+ } else {
+ return Timestamp.fromBits(0, low << (numBits - 32));
+ }
+ }
+};
+
+/**
+ * Returns this Timestamp with bits shifted to the right by the given amount.
+ *
+ * @param {Number} numBits the number of bits by which to shift.
+ * @return {Timestamp} this shifted to the right by the given amount.
+ * @api public
+ */
+Timestamp.prototype.shiftRight = function(numBits) {
+ numBits &= 63;
+ if (numBits == 0) {
+ return this;
+ } else {
+ var high = this.high_;
+ if (numBits < 32) {
+ var low = this.low_;
+ return Timestamp.fromBits(
+ (low >>> numBits) | (high << (32 - numBits)),
+ high >> numBits);
+ } else {
+ return Timestamp.fromBits(
+ high >> (numBits - 32),
+ high >= 0 ? 0 : -1);
+ }
+ }
+};
+
+/**
+ * Returns this Timestamp with bits shifted to the right by the given amount, with the new top bits matching the current sign bit.
+ *
+ * @param {Number} numBits the number of bits by which to shift.
+ * @return {Timestamp} this shifted to the right by the given amount, with zeros placed into the new leading bits.
+ * @api public
+ */
+Timestamp.prototype.shiftRightUnsigned = function(numBits) {
+ numBits &= 63;
+ if (numBits == 0) {
+ return this;
+ } else {
+ var high = this.high_;
+ if (numBits < 32) {
+ var low = this.low_;
+ return Timestamp.fromBits(
+ (low >>> numBits) | (high << (32 - numBits)),
+ high >>> numBits);
+ } else if (numBits == 32) {
+ return Timestamp.fromBits(high, 0);
+ } else {
+ return Timestamp.fromBits(high >>> (numBits - 32), 0);
+ }
+ }
+};
+
+/**
+ * Returns a Timestamp representing the given (32-bit) integer value.
+ *
+ * @param {Number} value the 32-bit integer in question.
+ * @return {Timestamp} the corresponding Timestamp value.
+ * @api public
+ */
+Timestamp.fromInt = function(value) {
+ if (-128 <= value && value < 128) {
+ var cachedObj = Timestamp.INT_CACHE_[value];
+ if (cachedObj) {
+ return cachedObj;
+ }
+ }
+
+ var obj = new Timestamp(value | 0, value < 0 ? -1 : 0);
+ if (-128 <= value && value < 128) {
+ Timestamp.INT_CACHE_[value] = obj;
+ }
+ return obj;
+};
+
+/**
+ * Returns a Timestamp representing the given value, provided that it is a finite number. Otherwise, zero is returned.
+ *
+ * @param {Number} value the number in question.
+ * @return {Timestamp} the corresponding Timestamp value.
+ * @api public
+ */
+Timestamp.fromNumber = function(value) {
+ if (isNaN(value) || !isFinite(value)) {
+ return Timestamp.ZERO;
+ } else if (value <= -Timestamp.TWO_PWR_63_DBL_) {
+ return Timestamp.MIN_VALUE;
+ } else if (value + 1 >= Timestamp.TWO_PWR_63_DBL_) {
+ return Timestamp.MAX_VALUE;
+ } else if (value < 0) {
+ return Timestamp.fromNumber(-value).negate();
+ } else {
+ return new Timestamp(
+ (value % Timestamp.TWO_PWR_32_DBL_) | 0,
+ (value / Timestamp.TWO_PWR_32_DBL_) | 0);
+ }
+};
+
+/**
+ * Returns a Timestamp representing the 64-bit integer that comes by concatenating the given high and low bits. Each is assumed to use 32 bits.
+ *
+ * @param {Number} lowBits the low 32-bits.
+ * @param {Number} highBits the high 32-bits.
+ * @return {Timestamp} the corresponding Timestamp value.
+ * @api public
+ */
+Timestamp.fromBits = function(lowBits, highBits) {
+ return new Timestamp(lowBits, highBits);
+};
+
+/**
+ * Returns a Timestamp representation of the given string, written using the given radix.
+ *
+ * @param {String} str the textual representation of the Timestamp.
+ * @param {Number} opt_radix the radix in which the text is written.
+ * @return {Timestamp} the corresponding Timestamp value.
+ * @api public
+ */
+Timestamp.fromString = function(str, opt_radix) {
+ if (str.length == 0) {
+ throw Error('number format error: empty string');
+ }
+
+ var radix = opt_radix || 10;
+ if (radix < 2 || 36 < radix) {
+ throw Error('radix out of range: ' + radix);
+ }
+
+ if (str.charAt(0) == '-') {
+ return Timestamp.fromString(str.substring(1), radix).negate();
+ } else if (str.indexOf('-') >= 0) {
+ throw Error('number format error: interior "-" character: ' + str);
+ }
+
+ // Do several (8) digits each time through the loop, so as to
+ // minimize the calls to the very expensive emulated div.
+ var radixToPower = Timestamp.fromNumber(Math.pow(radix, 8));
+
+ var result = Timestamp.ZERO;
+ for (var i = 0; i < str.length; i += 8) {
+ var size = Math.min(8, str.length - i);
+ var value = parseInt(str.substring(i, i + size), radix);
+ if (size < 8) {
+ var power = Timestamp.fromNumber(Math.pow(radix, size));
+ result = result.multiply(power).add(Timestamp.fromNumber(value));
+ } else {
+ result = result.multiply(radixToPower);
+ result = result.add(Timestamp.fromNumber(value));
+ }
+ }
+ return result;
+};
+
+// NOTE: Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the
+// from* methods on which they depend.
+
+
+/**
+ * A cache of the Timestamp representations of small integer values.
+ * @type {Object}
+ * @api private
+ */
+Timestamp.INT_CACHE_ = {};
+
+// NOTE: the compiler should inline these constant values below and then remove
+// these variables, so there should be no runtime penalty for these.
+
+/**
+ * Number used repeated below in calculations. This must appear before the
+ * first call to any from* function below.
+ * @type {number}
+ * @api private
+ */
+Timestamp.TWO_PWR_16_DBL_ = 1 << 16;
+
+/**
+ * @type {number}
+ * @api private
+ */
+Timestamp.TWO_PWR_24_DBL_ = 1 << 24;
+
+/**
+ * @type {number}
+ * @api private
+ */
+Timestamp.TWO_PWR_32_DBL_ = Timestamp.TWO_PWR_16_DBL_ * Timestamp.TWO_PWR_16_DBL_;
+
+/**
+ * @type {number}
+ * @api private
+ */
+Timestamp.TWO_PWR_31_DBL_ = Timestamp.TWO_PWR_32_DBL_ / 2;
+
+/**
+ * @type {number}
+ * @api private
+ */
+Timestamp.TWO_PWR_48_DBL_ = Timestamp.TWO_PWR_32_DBL_ * Timestamp.TWO_PWR_16_DBL_;
+
+/**
+ * @type {number}
+ * @api private
+ */
+Timestamp.TWO_PWR_64_DBL_ = Timestamp.TWO_PWR_32_DBL_ * Timestamp.TWO_PWR_32_DBL_;
+
+/**
+ * @type {number}
+ * @api private
+ */
+Timestamp.TWO_PWR_63_DBL_ = Timestamp.TWO_PWR_64_DBL_ / 2;
+
+/** @type {Timestamp} */
+Timestamp.ZERO = Timestamp.fromInt(0);
+
+/** @type {Timestamp} */
+Timestamp.ONE = Timestamp.fromInt(1);
+
+/** @type {Timestamp} */
+Timestamp.NEG_ONE = Timestamp.fromInt(-1);
+
+/** @type {Timestamp} */
+Timestamp.MAX_VALUE =
+ Timestamp.fromBits(0xFFFFFFFF | 0, 0x7FFFFFFF | 0);
+
+/** @type {Timestamp} */
+Timestamp.MIN_VALUE = Timestamp.fromBits(0, 0x80000000 | 0);
+
+/**
+ * @type {Timestamp}
+ * @api private
+ */
+Timestamp.TWO_PWR_24_ = Timestamp.fromInt(1 << 24);
+
+/**
+ * Expose.
+ */
+if(typeof window === 'undefined') {
+ exports.Timestamp = Timestamp;
+}
\ No newline at end of file
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..6f5b939
--- /dev/null
+++ b/server.js
@@ -0,0 +1,78 @@
+var WebSocketServer = require('websocket').server;
+// Setup of ports etc
+var port = 3000;
+// Create an express server instance
+var express = require('express');
+var app = express.createServer();
+var mongo = require('mongodb');
+
+// BSON decoder
+var BSON = mongo.BSONPure.BSON;
+
+// console.dir(BSON)
+
+// Set up the configuration for the express server
+app.configure(function() {
+ app.use(express.static(__dirname + "/public"));
+ app.set('views', __dirname);
+ app.set('view engine', 'ejs');
+});
+
+// Provide the bootstrap file
+app.get('/', function(req, res) {
+ res.render('index', { layout: false });
+});
+
+// Start on port 8000
+app.listen(port, function(err) {
+ console.log("= server listening on :: " + port);
+});
+
+// Keeps all the live connections
+var connections = [];
+
+// Websocket server
+var wsServer = new WebSocketServer({
+ httpServer: app,
+ // Firefox 7 alpha has a bug that drops the
+ // connection on large fragmented messages
+ fragmentOutgoingMessages: false
+});
+
+wsServer.on('request', function(request) {
+ // Accept the connection
+ var connection = request.accept('game', request.origin);
+ // Save the connection
+ connections.push(connection);
+
+ console.log(connection.remoteAddress + " connected - Protocol Version " + connection.websocketVersion);
+
+ // Handle closed connections
+ connection.on('close', function() {
+ console.log(connection.remoteAddress + " disconnected");
+ var index = connections.indexOf(connection);
+ // remove the connection from the pool
+ if(index !== -1) {
+ connections.splice(index, 1);
+ }
+ });
+
+ // Handle incoming messages
+ connection.on('message', function(message) {
+ console.log("-------------------------------------------------- received message")
+ console.dir(message)
+
+ var buffer = new Buffer(message.binaryData);
+ var object = BSON.deserialize(buffer);
+
+ console.log("-------------------------------------------------- object")
+ console.dir(object)
+
+ // rebroadcast command to all clients
+ connections.forEach(function(destination) {
+ destination.sendUTF(message.binaryData);
+ });
+ });
+});
+
+