Wolfenstein 3D browser version master
authorBrian Harris <brian@idsoftware.com>
Thu, 16 May 2013 16:34:31 +0000 (11:34 -0500)
committerBrian Harris <brian@idsoftware.com>
Thu, 16 May 2013 16:34:31 +0000 (11:34 -0500)
This is the code for the browser version of Wolfenstein 3D which is
playable on http://www.wolfenstein.com/game_NA.php

32 files changed:
COPYING.txt [new file with mode: 0644]
README.txt [new file with mode: 0644]
index.html [new file with mode: 0644]
js/actorai.js [new file with mode: 0644]
js/actors.js [new file with mode: 0644]
js/actstat.js [new file with mode: 0644]
js/ai.js [new file with mode: 0644]
js/angle.js [new file with mode: 0644]
js/areas.js [new file with mode: 0644]
js/doors.js [new file with mode: 0644]
js/episodes.js [new file with mode: 0644]
js/file.js [new file with mode: 0644]
js/game.js [new file with mode: 0644]
js/input.js [new file with mode: 0644]
js/level.js [new file with mode: 0644]
js/load.js [new file with mode: 0644]
js/maps.sample.js [new file with mode: 0644]
js/math.js [new file with mode: 0644]
js/menu.js [new file with mode: 0644]
js/player.js [new file with mode: 0644]
js/powerups.js [new file with mode: 0644]
js/pushwall.js [new file with mode: 0644]
js/random.js [new file with mode: 0644]
js/raycaster.js [new file with mode: 0644]
js/renderer.js [new file with mode: 0644]
js/requestanimframe.js [new file with mode: 0644]
js/sound.js [new file with mode: 0644]
js/sprites.js [new file with mode: 0644]
js/weapon.js [new file with mode: 0644]
js/wolf.js [new file with mode: 0644]
styles.css [new file with mode: 0644]
wolf3d.html [new file with mode: 0644]

diff --git a/COPYING.txt b/COPYING.txt
new file mode 100644 (file)
index 0000000..b3f64f0
--- /dev/null
@@ -0,0 +1,345 @@
+                  GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  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 Library 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.
+
+                    END OF TERMS AND CONDITIONS
+
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
\ No newline at end of file
diff --git a/README.txt b/README.txt
new file mode 100644 (file)
index 0000000..a38bc3c
--- /dev/null
@@ -0,0 +1,22 @@
+WOLF3D Browser Version GPL source release
+===============================================
+
+This file contains the following sections:
+
+GENERAL NOTES
+LICENSE
+
+GENERAL NOTES
+=============
+
+WOLF3D Browser Version is a free release, and can be downloaded from
+https://github.com/id-Software/wolf3d-browser
+
+This source release does not contain any game data, the game data remains subject to the original EULA and applicable law.
+
+
+LICENSE
+=======
+
+See COPYING.txt for the GNU GENERAL PUBLIC LICENSE.  If COPYING.txt does not accompany, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
diff --git a/index.html b/index.html
new file mode 100644 (file)
index 0000000..324f7f8
--- /dev/null
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html lang="en-US">
+<head>
+       <!--
+       Copyright (C) 2012 id Software LLC, a ZeniMax Media company. 
+       
+       This file is part of the WOLF3D Browser Version GPL Source Code. 
+       -->
+       <meta charset="UTF-8">
+       <title>Test Page</title>
+    <style>
+        body {
+            background-color : black;
+            text-align : center;
+        }
+        #wolf3d {
+            margin-top : 100px;
+            border : none;
+            width : 640px;
+            height : 400px;
+        }
+    </style>
+</head>
+<body>
+
+    <iframe id="wolf3d" src="wolf3d.html" mozallowfullscreen="true" webkitallowfullscreen="true">
+    </iframe>
+    
+    
+    <script>
+        window.onfocus = function() {
+            var game = window.frames[0];
+            if (game) {
+                game.focus();
+            }
+        };
+        window.oncontextmenu = function(e) {
+            if (e.preventDefault) {
+                e.preventDefault();
+            }
+            var game = window.frames[0];
+            if (game && game.Wolf && game.Wolf.Input) {
+                if (game.Wolf.Input.rightMouseDown()) {
+                    game.Wolf.Input.resetMouse();
+                }
+            }
+            return false;
+        }
+        window.onmouseup = function(e) {
+            var game = window.frames[0];
+            if (game && game.Wolf && game.Wolf.Input) {
+                if (game.Wolf.Input.rightMouseDown() || game.Wolf.Input.leftMouseDown()) {
+                    game.Wolf.Input.resetMouse();
+                    if (e.preventDefault) {
+                        e.preventDefault();
+                    }
+                    return false;
+                }
+            }
+        }
+    </script>
+    
+</body>
+</html>
\ No newline at end of file
diff --git a/js/actorai.js b/js/actorai.js
new file mode 100644 (file)
index 0000000..a1d3b4e
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+* ===========================================================================
+* 
+* Wolf3D Browser Version GPL Source Code
+* Copyright (C) 2012 id Software LLC, a ZeniMax Media company. 
+* 
+* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").  
+* 
+* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+* 
+* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+* 
+* You should have received a copy of the GNU General Public License version 2
+* along with Wolf3D Browser Source Code.  If not, see <http://www.gnu.org/licenses/>.
+* 
+* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
+* 
+* ===========================================================================
+*/
+
+
+
+/** 
+ * @namespace 
+ * @description Artificial intelligence
+ */
+Wolf.ActorAI = (function() {
+
+    var dsounds = [
+        "sfx/025.wav",
+        "sfx/026.wav",
+        "sfx/086.wav",
+        "sfx/088.wav",
+        "sfx/105.wav",
+        "sfx/107.wav",
+        "sfx/109.wav"
+    ]; 
+
+    /**
+     * @description Initiate death scream sound effect.
+     * @memberOf Wolf.ActorAI
+     * @param {object} self The enemy actor object.
+     * @param {object} game The game object.
+     */
+    function deathScream(self, game) {
+        var pos = game.player.position;
+        
+        switch (self.type) {
+            case Wolf.en_mutant:
+                Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/037.wav", 1, Wolf.ATTN_NORM, 0);
+                break;
+
+            case Wolf.en_guard:
+                Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, dsounds[Wolf.Random.rnd() % 6], 1, Wolf.ATTN_NORM, 0);
+                break;
+
+            case Wolf.en_officer:
+                Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/074.wav", 1, Wolf.ATTN_NORM, 0);
+                break;
+
+            case Wolf.en_ss:
+                Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/046.wav", 1, Wolf.ATTN_NORM, 0);
+                break;
+
+            case Wolf.en_dog:
+                Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/035.wav", 1, Wolf.ATTN_NORM, 0);
+                break;
+
+            case Wolf.en_boss:
+                Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/019.wav", 1, Wolf.ATTN_NORM, 0);
+                break;
+
+            case Wolf.en_schabbs:
+                Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/061.wav", 1, Wolf.ATTN_NORM, 0);            
+                break;
+
+            case Wolf.en_fake:
+                Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/069.wav", 1, Wolf.ATTN_NORM, 0);            
+                break;
+
+            case Wolf.en_mecha:
+                Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/084.wav", 1, Wolf.ATTN_NORM, 0);            
+                break;
+
+            case Wolf.en_hitler:
+                Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/044.wav", 1, Wolf.ATTN_NORM, 0);
+                break;
+
+            case Wolf.en_gretel:
+                Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/115.wav", 1, Wolf.ATTN_NORM, 0);
+                break;
+
+            case Wolf.en_gift:
+                Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/091.wav", 1, Wolf.ATTN_NORM, 0);            
+                break;
+
+            case Wolf.en_fat:
+                Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/119.wav", 1, Wolf.ATTN_NORM, 0);
+                break;
+        }
+    }
+
+    /* Hitler */
+    
+    /**
+     * @description Play Mecha sound.
+     * @memberOf Wolf.ActorAI
+     * @param {object} self The enemy actor object.
+     * @param {object} game The game object.
+     */
+    function mechaSound(self, game) {
+        if (game.level.state.areabyplayer[self.areanumber]) {
+            Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/080.wav", 1, Wolf.ATTN_NORM, 0);
+        }
+    }
+
+    
+    /**
+     * @description Play Slurpie sound.
+     * @memberOf Wolf.ActorAI
+     * @param {object} self The enemy actor object.
+     */
+    function slurpie(self, game) {
+        Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "lsfx/061.wav", 1, Wolf.ATTN_NORM, 0);
+    }
+
+
+    /**
+     * @description Spawn new actor, when Mecha Hitler is dead.
+     * @memberOf Wolf.ActorAI
+     * @param {object} self The enemy actor object.
+     * @param {object} game The game object.
+     */
+    function hitlerMorph(self, game) {
+        var hitpoints = [500, 700, 800, 900],
+            level = game.level,
+            hitler;
+
+        hitler = Wolf.Actors.getNewActor(level);
+        if(!hitler) {
+            return;
+        }
+
+        hitler.x = self.x;
+        hitler.y = self.y;
+        hitler.distance = self.distance;
+        hitler.tile.x = self.tile.x;
+        hitler.tile.y = self.tile.y;
+        hitler.angle = self.angle;
+        hitler.dir = self.dir;
+        hitler.health = hitpoints[game.skill];
+        hitler.areanumber = self.areanumber;
+        hitler.state = Wolf.st_chase1;
+        hitler.type = Wolf.en_hitler;
+        hitler.speed = Wolf.SPDPATROL * 5;
+        hitler.ticcount = 0;
+        hitler.flags = self.flags | Wolf.FL_SHOOTABLE;
+        hitler.sprite = Wolf.Sprites.getNewSprite(level);
+
+    }
+    
+    /* Angel of Death */
+    
+    var angel_temp = 0;
+
+    /**
+     * @description Play Angel of Death Breathing sound.
+     * @memberOf Wolf.ActorAI
+     * @param {object} self The enemy actor object.
+     */
+    function breathing(self) {
+        Wolf.Sound.startSound(null, null, 1, Wolf.CHAN_VOICE, "lsfx/080.wav", 1, Wolf.ATTN_NORM, 0);
+    }
+
+    
+    /**
+     * @description Reset Angel of Death attack counter
+     * @memberOf Wolf.ActorAI
+     * @param {object} self The enemy actor object.
+     */
+    function startAttack(self) {
+        angel_temp = 0;
+    }
+    
+
+    /**
+     * @description Angel of Death AI.
+     * @memberOf Wolf.ActorAI
+     * @param {object} self The enemy actor object.
+     */
+    function relaunch(self) {
+        if (++angel_temp == 3) {
+            Wolf.Actors.stateChange(self, Wolf.st_pain);
+            return;
+        }
+
+        if (Wolf.Random.rnd() & 1) {
+            Wolf.Actors.stateChange(self, Wolf.st_chase1);
+            return;
+        }
+    }
+
+    /**
+     * @description Victory - start intermission.
+     * @memberOf Wolf.ActorAI
+     */
+    function victory(game) {
+        Wolf.Game.startIntermission(game);
+    }
+
+    
+    /**
+     * @description Entity is dormant state. 
+     * @memberOf Wolf.ActorAI
+     * @param {object} self The enemy actor object.
+     * @param {object} game The game object.
+     */
+    function dormant(self, game) {
+        var level = game.level,
+            player = game.player,
+            deltax, 
+            deltay,
+            xl, xh, yl, yh, 
+            x, y, n,
+            moveok = false;
+
+        deltax = self.x - player.position.x;
+        deltay = self.y - player.position.y;
+        
+        if (deltax < -Wolf.MINACTORDIST || deltax > Wolf.MINACTORDIST) {
+            moveok = true;
+        } else if(deltay < -Wolf.MINACTORDIST || deltay > Wolf.MINACTORDIST) {
+            moveok = true;
+        }
+        
+        if (!moveok) {
+            return;
+        }
+
+        // moveok:
+        xl = (self.x - Wolf.MINDIST) >> Wolf.TILESHIFT;
+        xh = (self.x + Wolf.MINDIST) >> Wolf.TILESHIFT;
+        yl = (self.y - Wolf.MINDIST) >> Wolf.TILESHIFT;
+        yh = (self.y + Wolf.MINDIST) >> Wolf.TILESHIFT;
+
+        for(y = yl; y <= yh ; ++y ) {
+            for(x = xl;x <= xh;++x) {
+                if (level.tileMap[x][y] & Wolf.SOLID_TILE) {
+                    return;
+                }
+                for (n=0;n<level.state.numGuards;++n) {
+                    if (level.state.guards[n].state >= Wolf.st_die1) {
+                        continue;
+                    }
+                    if (level.state.guards[n].tile.x == x && level.state.guards[n].tile.y == y) {
+                        return; // another guard in path
+                    }
+                }
+            }
+        }
+
+        self.flags |= Wolf.FL_AMBUSH | Wolf.FL_SHOOTABLE;
+        self.flags &= ~Wolf.FL_ATTACKMODE;
+        self.dir = Wolf.Math.dir8_nodir;
+        Wolf.Actors.stateChange(self, Wolf.st_path1);
+    }
+
+
+    /**
+     * @description Death cam animation.
+     *              Tthe DeathCam feature isn't implimented, but we want to 
+     *              give the animation time to play before declaring victory.
+     * @memberOf Wolf.ActorAI
+     * @param {object} self The enemy actor object.
+     * @param {object} game The game object.
+     */
+    function startDeathCam(game, self) {
+        self.playstate = Wolf.ex_complete;
+        setTimeout(function() {
+            Wolf.Game.startIntermission(game);
+        }, 5000);
+    }
+    
+
+    /**
+     * @description Rockets emmit smoke.
+     * @memberOf Wolf.ActorAI
+     * @param {object} self The enemy actor object.
+     * @param {object} level The level object.
+     */
+    function smoke(self, game) {
+        var level = game.level,
+            smokeEnt = Wolf.Actors.getNewActor(level);
+        
+        if (!smokeEnt) {
+            return;
+        }
+
+        smokeEnt.x = self.x;
+        smokeEnt.y = self.y;
+        smokeEnt.tile.x = self.tile.x;
+        smokeEnt.tile.y = self.tile.y;
+        smokeEnt.state = Wolf.st_die1;
+        smokeEnt.type = (self.type == Wolf.en_hrocket) ? Wolf.en_hsmoke : Wolf.en_smoke;
+        smokeEnt.ticcount = 6;
+        smokeEnt.flags = Wolf.FL_NEVERMARK;
+        smokeEnt.sprite = Wolf.Sprites.getNewSprite(level);
+    }
+
+    
+
+    /**
+     * @description Puts an actor into attack mode and possibly reverses the direction if the player is behind it.
+     * @memberOf Wolf.ActorAI
+     * @param {object} self The enemy actor object.
+     */
+    function firstSighting(self, game) {
+        switch (self.type) {
+            case Wolf.en_guard:
+                Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/001.wav", 1, Wolf.ATTN_NORM, 0);
+                self.speed *= 3;    // go faster when chasing player
+                break;
+
+            case Wolf.en_officer:
+                Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/071.wav", 1, Wolf.ATTN_NORM, 0);
+                self.speed *= 5;    // go faster when chasing player
+                break;
+
+            case Wolf.en_mutant:
+                self.speed *= 3;    // go faster when chasing player
+                break;
+
+            case Wolf.en_ss:
+                Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/015.wav", 1, Wolf.ATTN_NORM, 0);    
+                self.speed *= 4;            // go faster when chasing player
+                break;
+
+            case Wolf.en_dog:
+                Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/002.wav", 1, Wolf.ATTN_NORM, 0);
+                self.speed *= 2;            // go faster when chasing player
+                break;
+
+            case Wolf.en_boss:
+                Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/017.wav", 1, Wolf.ATTN_NORM, 0);
+                self.speed = Wolf.SPDPATROL * 3;    // go faster when chasing player
+                break;
+
+            case Wolf.en_gretel:
+                Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/112.wav", 1, Wolf.ATTN_NORM, 0);
+                self.speed *= 3;            // go faster when chasing player
+                break;
+
+            case Wolf.en_gift:
+                Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/096.wav", 1, Wolf.ATTN_NORM, 0);
+                self.speed *= 3;            // go faster when chasing player
+                break;
+
+            case Wolf.en_fat:
+                Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/102.wav", 1, Wolf.ATTN_NORM, 0);
+                self.speed *= 3;            // go faster when chasing player
+                break;
+
+            case Wolf.en_schabbs:
+                Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/065.wav", 1, Wolf.ATTN_NORM, 0);
+                self.speed *= 3;            // go faster when chasing player
+                break;
+
+            case Wolf.en_fake:
+                Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/054.wav", 1, Wolf.ATTN_NORM, 0);
+                self.speed *= 3;            // go faster when chasing player
+                break;
+
+            case Wolf.en_mecha:
+                Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/040.wav", 1, Wolf.ATTN_NORM, 0);
+                self.speed *= 3;            // go faster when chasing player
+                break;
+
+            case Wolf.en_hitler:
+                Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/040.wav", 1, Wolf.ATTN_NORM, 0);
+                self.speed *= 5;            // go faster when chasing player
+                break;
+
+            case Wolf.en_blinky:
+            case Wolf.en_clyde:
+            case Wolf.en_pinky:
+            case Wolf.en_inky:
+                self.speed *= 2;            // go faster when chasing player
+                break;
+
+            default:
+                return;
+        }
+
+        Wolf.Actors.stateChange(self, Wolf.st_chase1);
+        
+        if (self.waitfordoorx) {
+            self.waitfordoorx = self.waitfordoory = 0;    // ignore the door opening command
+        }
+
+        self.dir = Wolf.Math.dir8_nodir;
+        self.flags |= Wolf.FL_ATTACKMODE | Wolf.FL_FIRSTATTACK;
+    }
+
+
+    /**
+     * @description Called when the player succesfully hits an enemy.
+     *              Does damage points to enemy ob, either putting it into a stun frame or killing it.
+     * @memberOf Wolf.ActorAI
+     * @param {object} self The enemy actor object.
+     * @param {object} game The game object.
+     * @param {object} player The player object.
+     * @param {number} damage The number of damage points.
+     */
+    function damageActor(self, game, player, damage) {
+        player.madenoise = 1;
+
+        // do double damage if shooting a non attack mode actor
+        if (!(self.flags & Wolf.FL_ATTACKMODE)) {
+            damage <<= 1;
+        }
+
+        self.health -= damage;
+
+        if (self.health <= 0) {
+            killActor(self, game, player);
+        } else {
+            if (!(self.flags & Wolf.FL_ATTACKMODE) ) {
+                firstSighting(self, game); // put into combat mode
+            }
+            switch (self.type) { // dogs only have one hit point
+                case Wolf.en_guard:
+                case Wolf.en_officer:
+                case Wolf.en_mutant:
+                case Wolf.en_ss:
+                    if (self.health & 1) {
+                        Wolf.Actors.stateChange(self, Wolf.st_pain);
+                    } else {
+                        Wolf.Actors.stateChange(self, Wolf.st_pain1);
+                    }
+                    break;
+            }
+        }
+    }
+    
+    
+    /**
+     * @description Actor has been killed, so give points and spawn powerups.
+     * @memberOf Wolf.ActorAI
+     * @param {object} self The enemy actor object.
+     * @param {object} game The game object.
+     * @param {object} player The player object.
+     */
+    function killActor(self, game, player) {
+        var level = game.level,
+            tilex = self.tile.x = self.x >> Wolf.TILESHIFT; // drop item on center, 
+            tiley = self.tile.y = self.y >> Wolf.TILESHIFT;
+
+        switch (self.type) {
+            case Wolf.en_guard:
+                Wolf.Player.givePoints(player, 100);
+                Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_clip2);
+                break;
+
+            case Wolf.en_officer:
+                Wolf.Player.givePoints(player, 400);
+                Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_clip2);
+                break;
+
+            case Wolf.en_mutant:
+                Wolf.Player.givePoints(player, 700);
+                Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_clip2);
+                break;
+
+            case Wolf.en_ss:
+                Wolf.Player.givePoints(player, 500);
+                if (player.items & Wolf.ITEM_WEAPON_3) { // have a schmeiser?
+                    Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_clip2);
+                } else {
+                    Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_machinegun);
+                }
+                break;
+
+            case Wolf.en_dog:
+                Wolf.Player.givePoints(player, 200);
+                break;
+
+            case Wolf.en_boss:
+                Wolf.Player.givePoints(player, 5000);
+                Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_key1);
+                break;
+
+            case Wolf.en_gretel:
+                Wolf.Player.givePoints(player, 5000);
+                Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_key1);
+                break;
+
+            case Wolf.en_gift:
+                Wolf.Player.givePoints(player, 5000);
+                startDeathCam(game, self);
+                break;
+
+            case Wolf.en_fat:
+                Wolf.Player.givePoints(player, 5000);
+                startDeathCam(game, self);
+                break;
+
+            case Wolf.en_schabbs:
+                Wolf.Player.givePoints(player, 5000);
+                deathScream(self, game);
+                startDeathCam(game, self);
+                break;
+
+            case Wolf.en_fake:
+                Wolf.Player.givePoints(player, 2000);
+                break;
+
+            case Wolf.en_mecha:
+                Wolf.Player.givePoints(player, 5000);
+                break;
+
+            case Wolf.en_hitler:
+                Wolf.Player.givePoints(player, 5000);
+                deathScream(self, game);
+                startDeathCam(game, self);
+                break;
+
+        }
+
+        Wolf.Actors.stateChange(self, Wolf.st_die1);
+        
+        if (++level.state.killedMonsters == level.state.totalMonsters) {
+            Wolf.Game.notify("You killed the last enemy!");
+        }
+        
+        self.flags &= ~Wolf.FL_SHOOTABLE;
+        self.flags |= Wolf.FL_NONMARK;
+    }
+    
+    return {
+        firstSighting : firstSighting,
+        damageActor : damageActor,
+        killActor : killActor,
+        deathScream : deathScream,
+        mechaSound : mechaSound,
+        slurpie : slurpie,
+        hitlerMorph : hitlerMorph,
+        breathing : breathing,
+        startAttack : startAttack,
+        relaunch : relaunch,
+        victory : victory,
+        dormant : dormant,
+        startDeathCam : startDeathCam,
+        smoke : smoke
+    };
+
+})();
\ No newline at end of file
diff --git a/js/actors.js b/js/actors.js
new file mode 100644 (file)
index 0000000..98e71ec
--- /dev/null
@@ -0,0 +1,518 @@
+/*
+* ===========================================================================
+* 
+* Wolf3D Browser Version GPL Source Code
+* Copyright (C) 2012 id Software LLC, a ZeniMax Media company. 
+* 
+* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").  
+* 
+* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+* 
+* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+* 
+* You should have received a copy of the GNU General Public License version 2
+* along with Wolf3D Browser Source Code.  If not, see <http://www.gnu.org/licenses/>.
+* 
+* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
+* 
+* ===========================================================================
+*/
+
+/** 
+ * @namespace 
+ * @description Actors
+ */
+Wolf.Actors = (function() {
+
+    Wolf.setConsts({
+        SPDPATROL       : 512,
+        SPDDOG          : 1500,
+
+        FL_SHOOTABLE    : 1,
+        FL_BONUS        : 2,
+        FL_NEVERMARK    : 4,
+        FL_VISABLE      : 8,
+        FL_ATTACKMODE   : 16,
+        FL_FIRSTATTACK  : 32,
+        FL_AMBUSH       : 64,
+        FL_NONMARK      : 128,
+
+        MAX_GUARDS      : 255,
+        NUMENEMIES      : 31,
+        NUMSTATES       : 34,
+
+        MINACTORDIST    : 0x10000
+    });
+    
+    Wolf.setConsts({
+        en_guard        : 0,
+        en_officer      : 1,
+        en_ss           : 2,
+        en_dog          : 3,
+        en_boss         : 4,
+        en_schabbs      : 5,
+        en_fake         : 6,
+        en_mecha        : 7,
+        en_hitler       : 8,
+        en_mutant       : 9,
+        en_blinky       : 10,
+        en_clyde        : 11,
+        en_pinky        : 12,
+        en_inky         : 13,
+        en_gretel       : 14,
+        en_gift         : 15,
+        en_fat          : 16,
+        // --- Projectiles
+        en_needle       : 17,
+        en_fire         : 18,
+        en_rocket       : 19,
+        en_smoke        : 20,
+        en_bj           : 21,
+        // --- Spear of destiny!
+        en_spark        : 22,
+        en_hrocket      : 23,
+        en_hsmoke       : 24,
+
+        en_spectre      : 25,
+        en_angel        : 26,
+        en_trans        : 27,
+        en_uber         : 28,
+        en_will         : 29,
+        en_death        : 30
+    });
+    
+    Wolf.setConsts({
+        st_stand        : 0,
+        st_path1        : 1,
+        st_path1s       : 2,
+        st_path2        : 3,
+        st_path3        : 4,
+        st_path3s       : 5,
+        st_path4        : 6,
+        st_pain         : 7,
+        st_pain1        : 8,
+        st_shoot1       : 9,
+        st_shoot2       : 10,
+        st_shoot3       : 11,
+        st_shoot4       : 12,
+        st_shoot5       : 13,
+        st_shoot6       : 14,
+        st_shoot7       : 15,
+        st_shoot8       : 16,
+        st_shoot9       : 17,
+        st_chase1       : 18,
+        st_chase1s      : 19,
+        st_chase2       : 20,
+        st_chase3       : 21,
+        st_chase3s      : 22,
+        st_chase4       : 23,
+        st_die1         : 24,
+        st_die2         : 25,
+        st_die3         : 26,
+        st_die4         : 27,
+        st_die5         : 28,
+        st_die6         : 29,
+        st_die7         : 30,
+        st_die8         : 31,
+        st_die9         : 32,
+        st_dead         : 33,
+        st_remove       : 34
+    });
+    
+    var add8dir = [4, 5, 6, 7, 0, 1, 2, 3, 0],
+        r_add8dir = [4, 7, 6, 5, 0, 1, 2, 3, 0];
+
+    /**
+     * @description Create new actor.
+     * @memberOf Wolf.Actors
+     * @param {object} level The level object.
+     * @returns {object} The new actor object.
+     */
+    function getNewActor(level) {
+
+        if (level.state.numGuards > Wolf.MAX_GUARDS) {
+            return null;
+        }
+
+        var actor = {
+            x : 0,
+            y : 0,
+            angle : 0,
+            type : 0,
+            health : 0,
+            max_health : 0,
+            speed : 0,
+            ticcount : 0,
+            temp2 : 0,
+            distance : 0,
+            tile : {
+                x : 0,
+                y : 0
+            },
+            areanumber : 0,
+            waitfordoorx : 0, 
+            waitfordoory : 0,   // waiting on this door if non 0
+            flags : 0,            //    FL_SHOOTABLE, etc
+            state : 0,
+            dir : 0,
+            sprite : 0
+        };
+        level.state.guards[level.state.numGuards++] = actor;
+        
+        return actor;
+    }
+    
+    /**
+     * @description Process a single actor.
+     * @private
+     * @param {object} ent The actor object.
+     * @param {object} level The level object.
+     * @param {object} player The player object.
+     * @param {number} tics The number of tics.
+     * @returns {boolean} False if actor should be removed, otherwise true.
+     */
+    function doGuard(ent, game, tics) { // FIXME: revise!
+        var think;
+
+        //assert( ent->tilex >= 0 && ent->tilex < 64 );
+        //assert( ent->tiley >= 0 && ent->tiley < 64 );
+        //assert( ent->dir >= 0 && ent->dir <= 8 );
+   
+   
+        // ticcounts fire discrete actions separate from think functions
+        if (ent.ticcount) {
+            ent.ticcount -= tics;
+
+            while (ent.ticcount <= 0) {
+                //assert( ent->type >= 0 && ent->type < NUMENEMIES );
+                //assert( ent->state >= 0 && ent->state < NUMSTATES );
+
+                think = Wolf.objstate[ent.type][ent.state].action; // end of state action
+                if (think) {
+                    think(ent, game, tics);
+                    if (ent.state == Wolf.st_remove) {
+                        return false;
+                    }
+                }
+
+                ent.state = Wolf.objstate[ent.type][ent.state].next_state;
+                if (ent.state == Wolf.st_remove) {
+                    return false;
+                }
+            
+                if (!Wolf.objstate[ent.type][ent.state].timeout) {
+                    ent.ticcount = 0;
+                    break;
+                }
+                
+                ent.ticcount += Wolf.objstate[ent.type][ent.state].timeout;
+            }
+        }
+        //
+        // think
+        //
+        //assert( ent->type >= 0 && ent->type < NUMENEMIES );
+        //assert( ent->state >= 0 && ent->state < NUMSTATES );
+        think = Wolf.objstate[ent.type][ent.state].think;
+
+        if (think) {
+            think(ent, game, tics);
+            if (ent.state == Wolf.st_remove) {
+                return false;
+            }
+        }
+        
+        return true;
+    }
+    
+
+    /**
+     * @description Changes guard's state to that defined in newState.
+     * @memberOf Wolf.Actors
+     * @param {object} ent The actor object.
+     * @param {number} newState The new state.
+     */
+    function stateChange(ent, newState) {
+        ent.state = newState;
+        // assert( ent->type >= 0 && ent->type < NUMENEMIES );
+        if (newState == Wolf.st_remove) {
+            ent.ticcount = 0;
+        } else {
+            // assert( ent->state >= 0 && ent->state < NUMSTATES );
+            ent.ticcount = Wolf.objstate[ent.type][ent.state].timeout; //0;
+        }
+    }
+    
+    /**
+     * @description Process all the enemy actors.
+     * @memberOf Wolf.Actors
+     * @param {object} level The level object.
+     * @param {object} player The player object.
+     * @param {number} tics The number of tics.
+     */
+    function process(game, tics) {
+        var level = game.level,
+            player = game.player,
+            n, tex, guard,
+            liveGuards = [];
+            
+        for (n = 0 ; n < level.state.numGuards ; ++n ) {
+            guard = level.state.guards[n];
+
+            if (!doGuard(guard, game, tics)) {
+                // remove guard from the game forever!
+                // remove(game, guards[n--]);
+                Wolf.Sprites.remove(level, guard.sprite);
+                level.state.guards[n] = null;
+                continue;
+            }
+
+            Wolf.Sprites.setPos(level, guard.sprite, guard.x, guard.y, guard.angle);
+            
+            tex = Wolf.objstate[guard.type][guard.state].texture;
+            
+            if (Wolf.objstate[guard.type][guard.state].rotate) {
+                if (guard.type == Wolf.en_rocket || guard.type == Wolf.en_hrocket) {
+                    tex += r_add8dir[Wolf.Math.get8dir( Wolf.Angle.distCW(Wolf.FINE2RAD(player.angle), Wolf.FINE2RAD(guard.angle)))];                
+                } else {
+                    tex += add8dir[Wolf.Math.get8dir( Wolf.Angle.distCW(Wolf.FINE2RAD(player.angle), Wolf.FINE2RAD(guard.angle)))];                
+                }
+            }
+            Wolf.Sprites.setTex(level, guard.sprite, 0, tex);
+        }
+
+        for (n = 0 ; n < level.state.numGuards ; ++n ) {
+            if (level.state.guards[n]) {
+                liveGuards.push(level.state.guards[n]);
+            }
+        }
+        level.state.guards = liveGuards;
+        level.state.numGuards = liveGuards.length;
+    }
+    
+    /**
+     * @description Reset and clear the enemy actors in the level.
+     * @memberOf Wolf.Actors
+     * @param {object} level The level object.
+     */
+    function resetGuards(level) {
+        level.state.guards = [];
+        level.state.numGuards = 0;
+        //New = NULL;
+    }
+
+    /**
+     * @description Spawn a new enemy actor at the given position.
+     * @memberOf Wolf.Actors
+     * @param {object} level The level object.
+     * @param {number} skill The difficulty level.
+     * @param {number} which The actor type.
+     * @param {number} x The x position.
+     * @param {number} y The y position.
+     * @param {number} dir The direction.
+     * @returns {object} The new actor object or null if actor creation failed.
+     */
+    function spawn(level, skill, which, x, y, dir) {
+        var ent = getNewActor(level);
+        
+        if (!ent) {
+            return null;
+        }
+
+        ent.x = Wolf.TILE2POS(x);
+        ent.y = Wolf.TILE2POS(y);
+
+        ent.tile.x = x;
+        ent.tile.y = y;
+
+        // assert( dir >= 0 && dir <= 4 );
+        ent.angle = Wolf.Math.dir4angle[dir];
+        ent.dir = Wolf.Math.dir4to8[dir];
+
+        ent.areanumber = level.areas[x][y];
+
+        if (ent.areanumber < 0) {
+            // ambush marker tiles are listed as -3 area
+            ent.areanumber = 0;
+        }
+    
+        // assert( ent->areanumber >= 0 && ent->areanumber < NUMAREAS );
+        ent.type = which;
+        ent.health = Wolf.starthitpoints[skill][which];
+        ent.sprite = Wolf.Sprites.getNewSprite(level);
+
+        return ent;
+    }
+    
+    /**
+     * @description Spawn a dead guard.
+     * @memberOf Wolf.Actors
+     * @param {object} level The level object.
+     * @param {number} skill The difficulty level.
+     * @param {number} which The actor type.
+     * @param {number} x The x position.
+     * @param {number} y The y position.
+     */
+    function spawnDeadGuard(level, skill, which, x, y) {
+        var self = spawn(level, skill, which, x, y, Wolf.Math.dir4_nodir);
+        if (!self) {
+            return;
+        }
+        self.state = Wolf.st_dead;
+        self.speed = 0;
+        self.health = 0;
+        self.ticcount = Wolf.objstate[which][Wolf.st_dead].timeout ? Wolf.Random.rnd() % Wolf.objstate[which][Wolf.st_dead].timeout + 1 : 0;
+    }
+    
+    /**
+     * @description Spawn a patrolling guard.
+     * @memberOf Wolf.Actors
+     * @param {object} level The level object.
+     * @param {number} skill The difficulty level.
+     * @param {number} which The actor type.
+     * @param {number} x The x position.
+     * @param {number} y The y position.
+     */
+    function spawnPatrol(level, skill, which, x, y, dir) {
+        var self = spawn(level, skill, which, x, y, dir);
+        if (!self) {
+            return;
+        }
+    
+        self.state = Wolf.st_path1;
+        self.speed = (which == Wolf.en_dog) ? Wolf.SPDDOG : Wolf.SPDPATROL;
+        self.distance = Wolf.TILEGLOBAL;
+        self.ticcount = Wolf.objstate[which][Wolf.st_path1].timeout ? Wolf.Random.rnd() % Wolf.objstate[which][Wolf.st_path1].timeout + 1 : 0;
+        self.flags |= Wolf.FL_SHOOTABLE;
+
+        level.state.totalMonsters++;
+    }
+
+    /**
+     * @description Spawn a standing guard.
+     * @memberOf Wolf.Actors
+     * @param {object} level The level object.
+     * @param {number} skill The difficulty level.
+     * @param {number} which The actor type.
+     * @param {number} x The x position.
+     * @param {number} y The y position.
+     */
+    function spawnStand(level, skill, which, x, y, dir) {
+        var self = spawn(level, skill, which, x, y, dir);
+        if (!self) {
+            return;
+        }
+
+        self.state = Wolf.st_stand;
+        self.speed = Wolf.SPDPATROL;
+        self.ticcount = Wolf.objstate[which][Wolf.st_stand].timeout ? Wolf.Random.rnd() % Wolf.objstate[which][Wolf.st_stand].timeout + 1 : 0;
+        self.flags |= Wolf.FL_SHOOTABLE;
+        
+        if (level.tileMap[x][y] & Wolf.AMBUSH_TILE) {
+            self.flags |= Wolf.FL_AMBUSH;
+        }
+
+        level.state.totalMonsters++;
+    }
+    
+    function spawnBoss(level, skill, which, x, y) {
+        var self,
+            face;
+
+        switch (which) {
+            case Wolf.en_boss:
+            case Wolf.en_schabbs:
+            case Wolf.en_fat:
+            case Wolf.en_hitler:
+                face = Wolf.Math.dir4_south;
+                break;
+
+            case Wolf.en_fake:
+            case Wolf.en_gretel:
+            case Wolf.en_gift:
+                face = Wolf.Math.dir4_north;
+                break;
+
+            case Wolf.en_trans:
+            case Wolf.en_uber:
+            case Wolf.en_will:
+            case Wolf.en_death:
+            case Wolf.en_angel:
+            case Wolf.en_spectre:
+                face = Wolf.Math.dir4_nodir;
+                break;
+
+            default:
+                face = Wolf.Math.dir4_nodir;
+                break;
+        }
+
+        self = spawn(level, skill, which, x, y, face);
+        if (!self) {
+            return;
+        }
+        
+        self.state = which == Wolf.en_spectre ? Wolf.st_path1 : Wolf.st_stand;
+        self.speed = Wolf.SPDPATROL;
+        self.health = Wolf.starthitpoints[skill][which];
+        self.ticcount = Wolf.objstate[which][Wolf.st_stand].timeout ? Wolf.Random.rnd() % Wolf.objstate[which][Wolf.st_stand].timeout + 1 : 0;
+        self.flags |= Wolf.FL_SHOOTABLE | Wolf.FL_AMBUSH;
+
+        level.state.totalMonsters++;
+    }
+    
+    
+    function spawnGhosts(level, skill, which, x, y) {
+        var self = spawn(level, skill, which, x, y, Wolf.Math.dir4_nodir);
+        if (!self) {
+            return;
+        }
+        
+        self.state = Wolf.st_chase1;
+        self.speed = Wolf.SPDPATROL * 3;
+        self.health = Wolf.starthitpoints[skill][which];
+        self.ticcount = Wolf.objstate[which][Wolf.st_chase1].timeout ? Wolf.Random.rnd() % Wolf.objstate[which][Wolf.st_chase1].timeout + 1: 0;
+        self.flags |= Wolf.FL_AMBUSH;
+
+        level.state.totalMonsters++;
+    }
+    
+    function spawnBJVictory(player, level, skill) {
+        var x = Wolf.POS2TILE(player.position.x),
+            y = Wolf.POS2TILE(player.position.y),
+            bj = spawn(level, skill, Wolf.en_bj, x, y + 1, Wolf.Math.dir4_north);
+        
+        if (!bj) {
+            return;
+        }
+
+        bj.x = player.position.x;
+        bj.y = player.position.y;
+        bj.state = Wolf.st_path1;
+        bj.speed = Wolf.BJRUNSPEED;
+        bj.flags = Wolf.FL_NONMARK; // FL_NEVERMARK;
+        bj.temp2 = 6;
+        bj.ticcount = 1;
+    }
+   
+    return {
+        process : process,
+        resetGuards : resetGuards,
+        getNewActor : getNewActor,
+        spawn : spawn,
+        spawnDeadGuard : spawnDeadGuard,
+        spawnPatrol : spawnPatrol,
+        spawnStand : spawnStand,
+        spawnBoss : spawnBoss,
+        spawnGhosts : spawnGhosts,
+        spawnBJVictory : spawnBJVictory,
+        stateChange : stateChange
+    };
+
+})();
diff --git a/js/actstat.js b/js/actstat.js
new file mode 100644 (file)
index 0000000..add6f49
--- /dev/null
@@ -0,0 +1,1631 @@
+/*
+* ===========================================================================
+* 
+* Wolf3D Browser Version GPL Source Code
+* Copyright (C) 2012 id Software LLC, a ZeniMax Media company. 
+* 
+* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").  
+* 
+* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+* 
+* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+* 
+* You should have received a copy of the GNU General Public License version 2
+* along with Wolf3D Browser Source Code.  If not, see <http://www.gnu.org/licenses/>.
+* 
+* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
+* 
+* ===========================================================================
+*/
+
+
+(function() {
+    var ST_INFO_NULL = [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_dead]
+
+    /*
+        1-if object can be rotated, 0 if one sprite for every direction
+        base object's state texture if rotation is on facing player
+        after how man frames change state to .next_state
+        what to do every frame
+        what to do once per state
+        next state
+    */
+
+    //var objstate[Wolf.NUMENEMIES][Wolf.NUMSTATES] = [
+    var objstate = [
+        // en_guard,
+        [
+            [ 1, Wolf.SPR_GRD_S_1,     0, Wolf.AI.T_Stand, null, Wolf.st_stand ], // Wolf.st_stand,
+
+            [ 1, Wolf.SPR_GRD_W1_1,    20,  Wolf.AI.T_Path, null, Wolf.st_path1s ], // Wolf.st_path1,
+            [ 1, Wolf.SPR_GRD_W1_1,     5,  null,           null, Wolf.st_path2  ], // Wolf.st_path1s,
+            [ 1, Wolf.SPR_GRD_W2_1,    15,  Wolf.AI.T_Path, null, Wolf.st_path3  ], // Wolf.st_path2,
+            [ 1, Wolf.SPR_GRD_W3_1,    20,  Wolf.AI.T_Path, null, Wolf.st_path3s ], // Wolf.st_path3,
+            [ 1, Wolf.SPR_GRD_W3_1,     5,  null,           null, Wolf.st_path4  ], // Wolf.st_path3s,
+            [ 1, Wolf.SPR_GRD_W4_1,    15,  Wolf.AI.T_Path, null, Wolf.st_path1  ], // Wolf.st_path4,
+
+            [ 0, Wolf.SPR_GRD_PAIN_1,    10, null,    null, Wolf.st_chase1],// Wolf.st_pain,
+            [ 0, Wolf.SPR_GRD_PAIN_2,    10, null,    null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [ 0, Wolf.SPR_GRD_SHOOT1,    20, null,    null,             Wolf.st_shoot2],// Wolf.st_shoot1,
+            [ 0, Wolf.SPR_GRD_SHOOT2,    20, null,    Wolf.AI.T_Shoot,  Wolf.st_shoot3],// Wolf.st_shoot2,
+            [ 0, Wolf.SPR_GRD_SHOOT3,    20, null,    null,             Wolf.st_chase1],// Wolf.st_shoot3,
+
+            [ 0, Wolf.SPR_DEMO,    0, null,    null, Wolf.st_chase1 ], // Wolf.st_shoot4,
+            [ 0, Wolf.SPR_DEMO,    0, null,    null, Wolf.st_chase1 ], // Wolf.st_shoot5,
+            [ 0, Wolf.SPR_DEMO,    0, null,    null, Wolf.st_chase1 ], // Wolf.st_shoot6,
+            [ 0, Wolf.SPR_DEMO,    0, null,    null, Wolf.st_chase1 ], // Wolf.st_shoot7,
+            [ 0, Wolf.SPR_DEMO,    0, null,    null, Wolf.st_chase1 ], // Wolf.st_shoot8,
+            [ 0, Wolf.SPR_DEMO,    0, null,    null, Wolf.st_chase1 ], // Wolf.st_shoot9,
+
+            [ 1, Wolf.SPR_GRD_W1_1, 10, Wolf.AI.T_Chase,    null, Wolf.st_chase1s ], // Wolf.st_chase1,
+            [ 1, Wolf.SPR_GRD_W1_1,  3, null,               null, Wolf.st_chase2  ], // Wolf.st_chase1s,
+            [ 1, Wolf.SPR_GRD_W2_1,  8, Wolf.AI.T_Chase,    null, Wolf.st_chase3  ], // Wolf.st_chase2,
+            [ 1, Wolf.SPR_GRD_W3_1, 10, Wolf.AI.T_Chase,    null, Wolf.st_chase3s ], // Wolf.st_chase3,
+            [ 1, Wolf.SPR_GRD_W3_1,  3, null,               null, Wolf.st_chase4  ], // Wolf.st_chase3s,
+            [ 1, Wolf.SPR_GRD_W4_1,  8, Wolf.AI.T_Chase,    null, Wolf.st_chase1  ], // Wolf.st_chase4,
+
+            [ 0, Wolf.SPR_GRD_DIE_1, 15, null, Wolf.ActorAI.deathScream,    Wolf.st_die2 ], // Wolf.st_die1,
+            [ 0, Wolf.SPR_GRD_DIE_2, 15, null, null,                        Wolf.st_die3 ], // Wolf.st_die2,
+            [ 0, Wolf.SPR_GRD_DIE_3, 15, null, null,                        Wolf.st_dead ], // Wolf.st_die3,
+
+            [ 0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead ], // Wolf.st_die4,
+            [ 0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead ], // Wolf.st_die5,
+            [ 0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead ], // Wolf.st_die6,
+            [ 0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead ], // Wolf.st_die7,
+            [ 0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead ], // Wolf.st_die8,
+            [ 0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead ], // Wolf.st_die9,
+
+            [ 0, Wolf.SPR_GRD_DEAD,    0, null,     null, Wolf.st_dead ] // Wolf.st_dead
+        ],
+        // en_officer,
+        [
+            [1, Wolf.SPR_OFC_S_1,   0,  Wolf.AI.T_Stand, null, Wolf.st_stand], // Wolf.st_stand,
+
+            [1, Wolf.SPR_OFC_W1_1,  20, Wolf.AI.T_Path, null, Wolf.st_path1s],// Wolf.st_path1,
+            [1, Wolf.SPR_OFC_W1_1,  5,  null,           null, Wolf.st_path2], // Wolf.st_path1s,
+            [1, Wolf.SPR_OFC_W2_1,  15, Wolf.AI.T_Path, null, Wolf.st_path3], // Wolf.st_path2,
+            [1, Wolf.SPR_OFC_W3_1,  20, Wolf.AI.T_Path, null, Wolf.st_path3s],// Wolf.st_path3,
+            [1, Wolf.SPR_OFC_W3_1,  5,  null,           null, Wolf.st_path4], // Wolf.st_path3s,
+            [1, Wolf.SPR_OFC_W4_1,  15, Wolf.AI.T_Path, null, Wolf.st_path1], // Wolf.st_path4,
+
+            [0, Wolf.SPR_OFC_PAIN_1,    10, null,     null, Wolf.st_chase1],// Wolf.st_pain,
+            [0, Wolf.SPR_OFC_PAIN_2,    10, null,     null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [0, Wolf.SPR_OFC_SHOOT1,     6, null,     null,             Wolf.st_shoot2],// Wolf.st_shoot1,
+            [0, Wolf.SPR_OFC_SHOOT2,    20, null,     Wolf.AI.T_Shoot,  Wolf.st_shoot3],// Wolf.st_shoot2,
+            [0, Wolf.SPR_OFC_SHOOT3,    10, null,     null,             Wolf.st_chase1],// Wolf.st_shoot3,
+
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot4,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot5,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot6,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot7,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot8,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot9,
+
+            [1, Wolf.SPR_OFC_W1_1, 10, Wolf.AI.T_Chase, null, Wolf.st_chase1s],// Wolf.st_chase1,
+            [1, Wolf.SPR_OFC_W1_1,  3, null,            null, Wolf.st_chase2], // Wolf.st_chase1s,
+            [1, Wolf.SPR_OFC_W2_1,  8, Wolf.AI.T_Chase, null, Wolf.st_chase3], // Wolf.st_chase2,
+            [1, Wolf.SPR_OFC_W3_1, 10, Wolf.AI.T_Chase, null, Wolf.st_chase3s],// Wolf.st_chase3,
+            [1, Wolf.SPR_OFC_W3_1,  3, null,            null, Wolf.st_chase4], // Wolf.st_chase3s,
+            [1, Wolf.SPR_OFC_W4_1,  8, Wolf.AI.T_Chase, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_OFC_DIE_1, 11, null, Wolf.ActorAI.deathScream, Wolf.st_die2],// Wolf.st_die1,
+            [0, Wolf.SPR_OFC_DIE_2, 11, null, null,                     Wolf.st_die3],// Wolf.st_die2,
+            [0, Wolf.SPR_OFC_DIE_3, 11, null, null,                     Wolf.st_dead],// Wolf.st_die3,
+
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die4,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die5,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die6,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die7,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die8,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die9,
+
+            [0, Wolf.SPR_OFC_DEAD,    0, null,     null, Wolf.st_dead] // Wolf.st_dead
+        ],
+        // en_ss,
+        [
+            [1, Wolf.SPR_SS_S_1,     0, Wolf.AI.T_Stand, null, Wolf.st_stand], // Wolf.st_stand,
+
+            [1, Wolf.SPR_SS_W1_1,   20, Wolf.AI.T_Path, null, Wolf.st_path1s],// Wolf.st_path1,
+            [1, Wolf.SPR_SS_W1_1,   5,  null,           null, Wolf.st_path2], // Wolf.st_path1s,
+            [1, Wolf.SPR_SS_W2_1,   15, Wolf.AI.T_Path, null, Wolf.st_path3], // Wolf.st_path2,
+            [1, Wolf.SPR_SS_W3_1,   20, Wolf.AI.T_Path, null, Wolf.st_path3s],// Wolf.st_path3,
+            [1, Wolf.SPR_SS_W3_1,   5,  null,           null, Wolf.st_path4], // Wolf.st_path3s,
+            [1, Wolf.SPR_SS_W4_1,   15, Wolf.AI.T_Path, null, Wolf.st_path1], // Wolf.st_path4,
+
+            [0, Wolf.SPR_SS_PAIN_1,    10, null,     null, Wolf.st_chase1],// Wolf.st_pain,
+            [0, Wolf.SPR_SS_PAIN_2,    10, null,     null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [0, Wolf.SPR_SS_SHOOT1, 20, null, null,             Wolf.st_shoot2],// Wolf.st_shoot1,
+            [0, Wolf.SPR_SS_SHOOT2, 20, null, Wolf.AI.T_Shoot,  Wolf.st_shoot3],// Wolf.st_shoot2,
+            [0, Wolf.SPR_SS_SHOOT3, 10, null, null,             Wolf.st_shoot4],// Wolf.st_shoot3,
+            [0, Wolf.SPR_SS_SHOOT2, 10, null, Wolf.AI.T_Shoot,  Wolf.st_shoot5],// Wolf.st_shoot4,
+            [0, Wolf.SPR_SS_SHOOT3, 10, null, null,             Wolf.st_shoot6],// Wolf.st_shoot5,
+            [0, Wolf.SPR_SS_SHOOT2, 10, null, Wolf.AI.T_Shoot,  Wolf.st_shoot7],// Wolf.st_shoot6,
+            [0, Wolf.SPR_SS_SHOOT3, 10, null, null,             Wolf.st_shoot8],// Wolf.st_shoot7,
+            [0, Wolf.SPR_SS_SHOOT2, 10, null, Wolf.AI.T_Shoot,  Wolf.st_shoot9],// Wolf.st_shoot8,
+            [0, Wolf.SPR_SS_SHOOT3, 10, null, null,             Wolf.st_chase1],// Wolf.st_shoot9,
+
+            [1, Wolf.SPR_SS_W1_1, 10, Wolf.AI.T_Chase,  null, Wolf.st_chase1s],// Wolf.st_chase1,
+            [1, Wolf.SPR_SS_W1_1,  3, null,             null, Wolf.st_chase2], // Wolf.st_chase1s,
+            [1, Wolf.SPR_SS_W2_1,  8, Wolf.AI.T_Chase,  null, Wolf.st_chase3], // Wolf.st_chase2,
+            [1, Wolf.SPR_SS_W3_1, 10, Wolf.AI.T_Chase,  null, Wolf.st_chase3s],// Wolf.st_chase3,
+            [1, Wolf.SPR_SS_W3_1,  3, null,             null, Wolf.st_chase4],     // Wolf.st_chase3s,
+            [1, Wolf.SPR_SS_W4_1,  8, Wolf.AI.T_Chase,  null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_SS_DIE_1, 15, null, Wolf.ActorAI.deathScream, Wolf.st_die2],// Wolf.st_die1,
+            [0, Wolf.SPR_SS_DIE_2, 15, null, null,                     Wolf.st_die3],// Wolf.st_die2,
+            [0, Wolf.SPR_SS_DIE_3, 15, null, null,                     Wolf.st_dead],// Wolf.st_die3,
+
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die4,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die5,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die6,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die7,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die8,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die9,
+
+            [0, Wolf.SPR_SS_DEAD,    0, null,     null, Wolf.st_dead] // Wolf.st_dead
+        ],
+        // en_dog,
+        [
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_stand], // Wolf.st_stand,
+
+            [1, Wolf.SPR_DOG_W1_1,      20, Wolf.AI.T_Path, null, Wolf.st_path1s],// Wolf.st_path1,
+            [1, Wolf.SPR_DOG_W1_1,      5,  null,           null, Wolf.st_path2], // Wolf.st_path1s,
+            [1, Wolf.SPR_DOG_W2_1,      15, Wolf.AI.T_Path, null, Wolf.st_path3], // Wolf.st_path2,
+            [1, Wolf.SPR_DOG_W3_1,      20, Wolf.AI.T_Path, null, Wolf.st_path3s],// Wolf.st_path3,
+            [1, Wolf.SPR_DOG_W3_1,      5,  null,           null, Wolf.st_path4], // Wolf.st_path3s,
+            [1, Wolf.SPR_DOG_W4_1,      15, Wolf.AI.T_Path, null, Wolf.st_path1], // Wolf.st_path4,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [0, Wolf.SPR_DOG_JUMP1, 10, null,     null,     Wolf.st_shoot2],// Wolf.st_shoot1,
+            [0, Wolf.SPR_DOG_JUMP2, 10, null,     Wolf.AI.T_Bite, Wolf.st_shoot3],// Wolf.st_shoot2,
+            [0, Wolf.SPR_DOG_JUMP3, 10, null,     null,   Wolf.st_shoot4],// Wolf.st_shoot3,
+            [0, Wolf.SPR_DOG_JUMP1, 10, null,     null,     Wolf.st_shoot5],// Wolf.st_shoot4,
+            [0, Wolf.SPR_DOG_W1_1,     10, null,     null,     Wolf.st_chase1],// Wolf.st_shoot5,
+
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot6,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot7,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot8,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot9,
+
+            [1, Wolf.SPR_DOG_W1_1, 10, Wolf.AI.T_DogChase, null, Wolf.st_chase1s],// Wolf.st_chase1,
+            [1, Wolf.SPR_DOG_W1_1,  3, null            , null, Wolf.st_chase2], // Wolf.st_chase1s,
+            [1, Wolf.SPR_DOG_W2_1,  8, Wolf.AI.T_DogChase, null, Wolf.st_chase3], // Wolf.st_chase2,
+            [1, Wolf.SPR_DOG_W3_1, 10, Wolf.AI.T_DogChase, null, Wolf.st_chase3s],// Wolf.st_chase3,
+            [1, Wolf.SPR_DOG_W3_1,  3, null            , null, Wolf.st_chase4],     // Wolf.st_chase3s,
+            [1, Wolf.SPR_DOG_W4_1,  8, Wolf.AI.T_DogChase, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_DOG_DIE_1, 15, null, Wolf.ActorAI.deathScream, Wolf.st_die2],// Wolf.st_die1,
+            [0, Wolf.SPR_DOG_DIE_2, 15, null, null,                    Wolf.st_die3],// Wolf.st_die2,
+            [0, Wolf.SPR_DOG_DIE_3, 15, null, null,                    Wolf.st_dead],// Wolf.st_die3,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die4,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die5,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die6,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die9,
+
+            [0, Wolf.SPR_DOG_DEAD,    0, null, null, Wolf.st_dead] // Wolf.st_dead
+        ],
+        // en_boss,
+        [
+            [0, Wolf.SPR_BOSS_W1,    0, Wolf.AI.T_Stand, null, Wolf.st_stand], // Wolf.st_stand,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1s],// Wolf.st_path1,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path2], // Wolf.st_path1s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3], // Wolf.st_path2,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3s],// Wolf.st_path3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path4], // Wolf.st_path3s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1], // Wolf.st_path4,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [0, Wolf.SPR_BOSS_SHOOT1, 30, null, null,        Wolf.st_shoot2],// Wolf.st_shoot1,
+            [0, Wolf.SPR_BOSS_SHOOT2, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot3],// Wolf.st_shoot2,
+            [0, Wolf.SPR_BOSS_SHOOT3, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot4],// Wolf.st_shoot3,
+            [0, Wolf.SPR_BOSS_SHOOT2, 10, null, Wolf.AI.T_Shoot,    Wolf.st_shoot5],// Wolf.st_shoot4,
+            [0, Wolf.SPR_BOSS_SHOOT3, 10, null, Wolf.AI.T_Shoot,    Wolf.st_shoot6],// Wolf.st_shoot5,
+            [0, Wolf.SPR_BOSS_SHOOT2, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot7],// Wolf.st_shoot6,
+            [0, Wolf.SPR_BOSS_SHOOT3, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot8],// Wolf.st_shoot7,
+            [0, Wolf.SPR_BOSS_SHOOT1, 10, null, null,        Wolf.st_chase1],// Wolf.st_shoot8,
+
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot9,
+
+            [0, Wolf.SPR_BOSS_W1, 10, Wolf.AI.T_Chase, null, Wolf.st_chase1s],// Wolf.st_chase1,
+            [0, Wolf.SPR_BOSS_W1,  3, null        , null, Wolf.st_chase2], // Wolf.st_chase1s,
+            [0, Wolf.SPR_BOSS_W2,  8, Wolf.AI.T_Chase, null, Wolf.st_chase3], // Wolf.st_chase2,
+            [0, Wolf.SPR_BOSS_W3, 10, Wolf.AI.T_Chase, null, Wolf.st_chase3s],// Wolf.st_chase3,
+            [0, Wolf.SPR_BOSS_W3,  3, null        , null, Wolf.st_chase4],     // Wolf.st_chase3s,
+            [0, Wolf.SPR_BOSS_W4,  8, Wolf.AI.T_Chase, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_BOSS_DIE1, 15, null, Wolf.ActorAI.deathScream, Wolf.st_die2],// Wolf.st_die1,
+            [0, Wolf.SPR_BOSS_DIE2, 15, null, null,                    Wolf.st_die3],// Wolf.st_die2,
+            [0, Wolf.SPR_BOSS_DIE3, 15, null, null,                    Wolf.st_dead],// Wolf.st_die3,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die4,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die5,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die6,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die9,
+
+            [0, Wolf.SPR_BOSS_DEAD, 0, null, null, Wolf.st_dead] // Wolf.st_dead
+        ],
+        // en_schabbs,
+        [
+            [0, Wolf.SPR_SCHABB_W1,    0, Wolf.AI.T_Stand, null, Wolf.st_stand], // Wolf.st_stand,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1s],// Wolf.st_path1,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path2], // Wolf.st_path1s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3], // Wolf.st_path2,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3s],// Wolf.st_path3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path4], // Wolf.st_path3s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1], // Wolf.st_path4,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [0, Wolf.SPR_SCHABB_SHOOT1, 30, null, null,     Wolf.st_shoot2],// Wolf.st_shoot1,
+            [0, Wolf.SPR_SCHABB_SHOOT2, 10, null, Wolf.AI.T_Launch, Wolf.st_chase1],// Wolf.st_shoot2,
+            
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot3,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],//  Wolf.st_shoot4,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot5,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot6,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot7,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot8,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot9,
+
+            [0, Wolf.SPR_SCHABB_W1, 10, Wolf.AI.T_BossChase, null, Wolf.st_chase1s],// Wolf.st_chase1,
+            [0, Wolf.SPR_SCHABB_W1,  3, null            , null, Wolf.st_chase2], // Wolf.st_chase1s,
+            [0, Wolf.SPR_SCHABB_W2,  8, Wolf.AI.T_BossChase, null, Wolf.st_chase3], // Wolf.st_chase2,
+            [0, Wolf.SPR_SCHABB_W3, 10, Wolf.AI.T_BossChase, null, Wolf.st_chase3s],// Wolf.st_chase3,
+            [0, Wolf.SPR_SCHABB_W3,  3, null            , null, Wolf.st_chase4],     // Wolf.st_chase3s,
+            [0, Wolf.SPR_SCHABB_W4,  8, Wolf.AI.T_BossChase, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_SCHABB_W1,        10, null, Wolf.ActorAI.deathScream, Wolf.st_die2],// Wolf.st_die1,
+            [0, Wolf.SPR_SCHABB_W1,        10, null, null,                     Wolf.st_die3],// Wolf.st_die2,
+            [0, Wolf.SPR_SCHABB_DIE1,    10, null, null,                     Wolf.st_die4],// Wolf.st_die3,
+            [0, Wolf.SPR_SCHABB_DIE2,    10, null, null,                     Wolf.st_die5],// Wolf.st_die4,
+            [0, Wolf.SPR_SCHABB_DIE3,    10, null, null,                     Wolf.st_dead],// Wolf.st_die5,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die6,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die9,
+
+            [0, Wolf.SPR_SCHABB_DEAD, 0, null, Wolf.ActorAI.startDeathCam, Wolf.st_dead] // Wolf.st_dead
+        ],
+        // en_fake,
+        [
+            [0, Wolf.SPR_FAKE_W1,    0, Wolf.AI.T_Stand, null, Wolf.st_stand], // Wolf.st_stand,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1s],// Wolf.st_path1,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path2], // Wolf.st_path1s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3], // Wolf.st_path2,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3s],// Wolf.st_path3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path4], // Wolf.st_path3s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1], // Wolf.st_path4,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [0, Wolf.SPR_FAKE_SHOOT,    8, null, Wolf.AI.T_Launch, Wolf.st_shoot2],// Wolf.st_shoot1,
+            [0, Wolf.SPR_FAKE_SHOOT,    8, null, Wolf.AI.T_Launch, Wolf.st_shoot3],// Wolf.st_shoot2,
+            [0, Wolf.SPR_FAKE_SHOOT,    8, null, Wolf.AI.T_Launch, Wolf.st_shoot4],// Wolf.st_shoot3,
+            [0, Wolf.SPR_FAKE_SHOOT,    8, null, Wolf.AI.T_Launch, Wolf.st_shoot5],// Wolf.st_shoot4,
+            [0, Wolf.SPR_FAKE_SHOOT,    8, null, Wolf.AI.T_Launch, Wolf.st_shoot6],// Wolf.st_shoot4,
+            [0, Wolf.SPR_FAKE_SHOOT,    8, null, Wolf.AI.T_Launch, Wolf.st_shoot7],// Wolf.st_shoot4,
+            [0, Wolf.SPR_FAKE_SHOOT,    8, null, Wolf.AI.T_Launch, Wolf.st_shoot8],// Wolf.st_shoot4,
+            [0, Wolf.SPR_FAKE_SHOOT,    8, null, Wolf.AI.T_Launch, Wolf.st_shoot9],// Wolf.st_shoot4,
+            [0, Wolf.SPR_FAKE_SHOOT,    8, null, null,             Wolf.st_chase1],// Wolf.st_shoot4,
+
+            [0, Wolf.SPR_FAKE_W1, 10, Wolf.AI.T_Fake,   null, Wolf.st_chase1s],// Wolf.st_chase1,
+            [0, Wolf.SPR_FAKE_W1,  3, null,             null, Wolf.st_chase2], // Wolf.st_chase1s,
+            [0, Wolf.SPR_FAKE_W2,  8, Wolf.AI.T_Fake,   null, Wolf.st_chase3], // Wolf.st_chase2,
+            [0, Wolf.SPR_FAKE_W3, 10, Wolf.AI.T_Fake,   null, Wolf.st_chase3s],// Wolf.st_chase3,
+            [0, Wolf.SPR_FAKE_W3,  3, null,             null, Wolf.st_chase4], // Wolf.st_chase3s,
+            [0, Wolf.SPR_FAKE_W4,  8, Wolf.AI.T_Fake,   null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_FAKE_DIE1, 10, null, Wolf.ActorAI.deathScream, Wolf.st_die2],// Wolf.st_die1,
+            [0, Wolf.SPR_FAKE_DIE2, 10, null, null,                     Wolf.st_die3],// Wolf.st_die2,
+            [0, Wolf.SPR_FAKE_DIE3, 10, null, null,                     Wolf.st_die4],// Wolf.st_die3,
+            [0, Wolf.SPR_FAKE_DIE4, 10, null, null,                     Wolf.st_die5],// Wolf.st_die4,
+            [0, Wolf.SPR_FAKE_DIE5, 10, null, null,                     Wolf.st_dead],// Wolf.st_die5,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die6,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die9,
+
+            [0, Wolf.SPR_FAKE_DEAD, 0, null, null, Wolf.st_dead] // Wolf.st_dead
+        ],
+        // en_hitler, (mecha)
+        [
+            [0, Wolf.SPR_MECHA_W1,    0, Wolf.AI.T_Stand, null, Wolf.st_stand], // Wolf.st_stand,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1s],// Wolf.st_path1,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path2], // Wolf.st_path1s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3], // Wolf.st_path2,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3s],// Wolf.st_path3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path4], // Wolf.st_path3s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1], // Wolf.st_path4,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [0, Wolf.SPR_MECHA_SHOOT1, 30, null, null, Wolf.st_shoot2],// Wolf.st_shoot1,
+            [0, Wolf.SPR_MECHA_SHOOT2, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot3],// Wolf.st_shoot2,
+            [0, Wolf.SPR_MECHA_SHOOT3, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot4],// Wolf.st_shoot3,
+            [0, Wolf.SPR_MECHA_SHOOT2, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot5],// Wolf.st_shoot4,
+            [0, Wolf.SPR_MECHA_SHOOT3, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot6],// Wolf.st_shoot5,
+            [0, Wolf.SPR_MECHA_SHOOT2, 10, null, Wolf.AI.T_Shoot, Wolf.st_chase1],// Wolf.st_shoot6,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_shoot8],// Wolf.st_shoot7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_shoot9],// Wolf.st_shoot8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_shoot9,
+
+            [0, Wolf.SPR_MECHA_W1, 10, Wolf.AI.T_Chase, Wolf.ActorAI.mechaSound, Wolf.st_chase1s],// Wolf.st_chase1,
+            [0, Wolf.SPR_MECHA_W1,  6, null     , null, Wolf.st_chase2], // Wolf.st_chase1s,
+            [0, Wolf.SPR_MECHA_W2,  8, Wolf.AI.T_Chase, null, Wolf.st_chase3], // Wolf.st_chase2,
+            [0, Wolf.SPR_MECHA_W3, 10, Wolf.AI.T_Chase, Wolf.ActorAI.mechaSound, Wolf.st_chase3s],// Wolf.st_chase3,
+            [0, Wolf.SPR_MECHA_W3,  6, null     , null, Wolf.st_chase4],     // Wolf.st_chase3s,
+            [0, Wolf.SPR_MECHA_W4,  8, Wolf.AI.T_Chase, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_MECHA_DIE1, 10, null, Wolf.ActorAI.deathScream, Wolf.st_die2],// Wolf.st_die1,
+            [0, Wolf.SPR_MECHA_DIE2, 10, null, null,                     Wolf.st_die3],// Wolf.st_die2,
+            [0, Wolf.SPR_MECHA_DIE3, 10, null, Wolf.ActorAI.hitlerMorph, Wolf.st_dead],// Wolf.st_die3,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die4,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die5,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die6,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die9,
+
+            [0, Wolf.SPR_MECHA_DEAD, 0, null, null, Wolf.st_dead] // Wolf.st_dead
+        ],
+        // en_hitler,
+        [
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_stand], // Wolf.st_stand,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1s],// Wolf.st_path1,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path2], // Wolf.st_path1s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3], // Wolf.st_path2,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3s],// Wolf.st_path3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path4], // Wolf.st_path3s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1], // Wolf.st_path4,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [0, Wolf.SPR_HITLER_SHOOT1, 30, null, null, Wolf.st_shoot2],// Wolf.st_shoot1,
+            [0, Wolf.SPR_HITLER_SHOOT2, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot3],// Wolf.st_shoot2,
+            [0, Wolf.SPR_HITLER_SHOOT3, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot4],// Wolf.st_shoot3,
+            [0, Wolf.SPR_HITLER_SHOOT2, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot5],// Wolf.st_shoot4,
+            [0, Wolf.SPR_HITLER_SHOOT3, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot6],// Wolf.st_shoot5,
+            [0, Wolf.SPR_HITLER_SHOOT2, 10, null, Wolf.AI.T_Shoot, Wolf.st_chase1],// Wolf.st_shoot6,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_shoot8],// Wolf.st_shoot7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_shoot9],// Wolf.st_shoot8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_shoot9,
+
+            [0, Wolf.SPR_HITLER_W1, 6, Wolf.AI.T_Chase, null, Wolf.st_chase1s], // Wolf.st_chase1,
+            [0, Wolf.SPR_HITLER_W1, 4, null     , null, Wolf.st_chase2],  // Wolf.st_chase1s,
+            [0, Wolf.SPR_HITLER_W2, 2, Wolf.AI.T_Chase, null, Wolf.st_chase3],  // Wolf.st_chase2,
+            [0, Wolf.SPR_HITLER_W3, 6, Wolf.AI.T_Chase, null, Wolf.st_chase3s], // Wolf.st_chase3,
+            [0, Wolf.SPR_HITLER_W3, 4, null        , null, Wolf.st_chase4], // Wolf.st_chase3s,
+            [0, Wolf.SPR_HITLER_W4, 2, Wolf.AI.T_Chase, null, Wolf.st_chase1],  // Wolf.st_chase4,
+
+            [0, Wolf.SPR_HITLER_W1,    1, null, Wolf.ActorAI.deathScream,  Wolf.st_die2],// Wolf.st_die1,
+            [0, Wolf.SPR_HITLER_W1,     10, null, null, Wolf.st_die3],// Wolf.st_die2,
+            [0, Wolf.SPR_HITLER_DIE1, 10, null, null, Wolf.st_die4],// Wolf.st_die3,
+            [0, Wolf.SPR_HITLER_DIE2, 10, null, null, Wolf.st_die5],// Wolf.st_die4,
+            [0, Wolf.SPR_HITLER_DIE3, 10, null, null, Wolf.st_die6],// Wolf.st_die5,
+            [0, Wolf.SPR_HITLER_DIE4, 10, null, null, Wolf.st_die7],// Wolf.st_die6,
+            [0, Wolf.SPR_HITLER_DIE5, 10, null, null, Wolf.st_die8],// Wolf.st_die7,
+            [0, Wolf.SPR_HITLER_DIE6, 10, null, null, Wolf.st_die9],// Wolf.st_die8,
+            [0, Wolf.SPR_HITLER_DIE7, 10, null, null, Wolf.st_dead],// Wolf.st_die9,
+
+            [0, Wolf.SPR_HITLER_DEAD, 0, null, Wolf.ActorAI.startDeathCam, Wolf.st_dead] // Wolf.st_dead
+        ],
+        // en_mutant,
+        [
+            [1, Wolf.SPR_MUT_S_1,     0, Wolf.AI.T_Stand, null, Wolf.st_stand], // Wolf.st_stand,
+
+            [1, Wolf.SPR_MUT_W1_1,    20, Wolf.AI.T_Path,     null, Wolf.st_path1s],// Wolf.st_path1,
+            [1, Wolf.SPR_MUT_W1_1,  5, null    ,  null, Wolf.st_path2], // Wolf.st_path1s,
+            [1, Wolf.SPR_MUT_W2_1, 15, Wolf.AI.T_Path,  null, Wolf.st_path3], // Wolf.st_path2,
+            [1, Wolf.SPR_MUT_W3_1,    20, Wolf.AI.T_Path,     null, Wolf.st_path3s],// Wolf.st_path3,
+            [1, Wolf.SPR_MUT_W3_1,     5, null    ,     null, Wolf.st_path4], // Wolf.st_path3s,
+            [1, Wolf.SPR_MUT_W4_1,    15, Wolf.AI.T_Path,     null, Wolf.st_path1], // Wolf.st_path4,
+
+            [0, Wolf.SPR_MUT_PAIN_1,    10, null,     null, Wolf.st_chase1],// Wolf.st_pain,
+            [0, Wolf.SPR_MUT_PAIN_2,    10, null,     null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [0, Wolf.SPR_MUT_SHOOT1,     6, null, Wolf.AI.T_Shoot, Wolf.st_shoot2], // Wolf.st_shoot1,
+            [0, Wolf.SPR_MUT_SHOOT2,    20, null, null,         Wolf.st_shoot3], // Wolf.st_shoot2,
+            [0, Wolf.SPR_MUT_SHOOT3,    10, null, Wolf.AI.T_Shoot, Wolf.st_shoot4], // Wolf.st_shoot3,
+            [0, Wolf.SPR_MUT_SHOOT4,    20, null, null,         Wolf.st_chase1], // Wolf.st_shoot4,
+
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot5,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot6,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot7,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot8,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot9,
+
+            [1, Wolf.SPR_MUT_W1_1, 10, Wolf.AI.T_Chase, null, Wolf.st_chase1s],// Wolf.st_chase1,
+            [1, Wolf.SPR_MUT_W1_1,  3, null     , null, Wolf.st_chase2], // Wolf.st_chase1s,
+            [1, Wolf.SPR_MUT_W2_1,  8, Wolf.AI.T_Chase, null, Wolf.st_chase3], // Wolf.st_chase2,
+            [1, Wolf.SPR_MUT_W3_1, 10, Wolf.AI.T_Chase, null, Wolf.st_chase3s],// Wolf.st_chase3,
+            [1, Wolf.SPR_MUT_W3_1,  3, null     , null, Wolf.st_chase4],     // Wolf.st_chase3s,
+            [1, Wolf.SPR_MUT_W4_1,  8, Wolf.AI.T_Chase, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_MUT_DIE_1, 7, null, Wolf.ActorAI.deathScream, Wolf.st_die2],// Wolf.st_die1,
+            [0, Wolf.SPR_MUT_DIE_2, 7, null, null,                     Wolf.st_die3],// Wolf.st_die2,
+            [0, Wolf.SPR_MUT_DIE_3, 7, null, null,                     Wolf.st_die4],// Wolf.st_die3,
+            [0, Wolf.SPR_MUT_DIE_4, 7, null, null,                     Wolf.st_dead],// Wolf.st_die4,
+
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die5,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die6,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die7,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die8,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_dead],// Wolf.st_die9,
+
+            [0, Wolf.SPR_MUT_DEAD,    0, null,     null, Wolf.st_dead] // Wolf.st_dead
+        ],
+        // en_blinky,
+        [
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_stand], // Wolf.st_stand,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1s],// Wolf.st_path1,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path2], // Wolf.st_path1s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3], // Wolf.st_path2,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3s],// Wolf.st_path3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path4], // Wolf.st_path3s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1], // Wolf.st_path4,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot2],// Wolf.st_shoot1,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot3],// Wolf.st_shoot2,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot4],// Wolf.st_shoot3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot5],// Wolf.st_shoot4,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot6],// Wolf.st_shoot5,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_chase1],// Wolf.st_shoot6,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_shoot8],// Wolf.st_shoot7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_shoot9],// Wolf.st_shoot8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_shoot9,
+
+            [0, Wolf.SPR_BLINKY_W1, 10, Wolf.AI.T_Ghosts, null, Wolf.st_chase2],// Wolf.st_chase1,
+            [0, Wolf.SPR_DEMO,  0, null, null, Wolf.st_chase2],            // Wolf.st_chase1s,
+            [0, Wolf.SPR_BLINKY_W2, 10, Wolf.AI.T_Ghosts, null, Wolf.st_chase1],// Wolf.st_chase2,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_chase3s],// Wolf.st_chase3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_chase4],     // Wolf.st_chase3s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_DEMO, 10, null, null, Wolf.st_die2],// Wolf.st_die1,
+            [0, Wolf.SPR_DEMO, 10, null, null, Wolf.st_die3],// Wolf.st_die2,
+            [0, Wolf.SPR_DEMO, 10, null, null, Wolf.st_dead],// Wolf.st_die3,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die4,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die5,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die6,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die9,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_dead] // Wolf.st_dead
+        ],
+        // en_clyde,
+        [
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_stand], // Wolf.st_stand,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1s],// Wolf.st_path1,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path2], // Wolf.st_path1s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3], // Wolf.st_path2,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3s],// Wolf.st_path3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path4], // Wolf.st_path3s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1], // Wolf.st_path4,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot2],// Wolf.st_shoot1,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot3],// Wolf.st_shoot2,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot4],// Wolf.st_shoot3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot5],// Wolf.st_shoot4,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot6],// Wolf.st_shoot5,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_chase1],// Wolf.st_shoot6,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_shoot8],// Wolf.st_shoot7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_shoot9],// Wolf.st_shoot8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_shoot9,
+
+            [0, Wolf.SPR_CLYDE_W1, 10, Wolf.AI.T_Ghosts, null, Wolf.st_chase2],// Wolf.st_chase1,
+            [0, Wolf.SPR_DEMO,  0, null, null, Wolf.st_chase2],            // Wolf.st_chase1s,
+            [0, Wolf.SPR_CLYDE_W2, 10, Wolf.AI.T_Ghosts, null, Wolf.st_chase1],// Wolf.st_chase2,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_chase3s],// Wolf.st_chase3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_chase4],     // Wolf.st_chase3s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_DEMO, 10, null, null, Wolf.st_die2],// Wolf.st_die1,
+            [0, Wolf.SPR_DEMO, 10, null, null, Wolf.st_die3],// Wolf.st_die2,
+            [0, Wolf.SPR_DEMO, 10, null, null, Wolf.st_dead],// Wolf.st_die3,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die4,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die5,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die6,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die9,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_dead] // Wolf.st_dead
+        ],
+        // en_pinky,
+        [
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_stand], // Wolf.st_stand,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1s],// Wolf.st_path1,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path2], // Wolf.st_path1s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3], // Wolf.st_path2,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3s],// Wolf.st_path3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path4], // Wolf.st_path3s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1], // Wolf.st_path4,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot2],// Wolf.st_shoot1,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot3],// Wolf.st_shoot2,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot4],// Wolf.st_shoot3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot5],// Wolf.st_shoot4,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot6],// Wolf.st_shoot5,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_chase1],// Wolf.st_shoot6,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_shoot8],// Wolf.st_shoot7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_shoot9],// Wolf.st_shoot8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_shoot9,
+
+            [0, Wolf.SPR_PINKY_W1, 10, Wolf.AI.T_Ghosts, null, Wolf.st_chase2],// Wolf.st_chase1,
+            [0, Wolf.SPR_DEMO,  0, null, null, Wolf.st_chase2],            // Wolf.st_chase1s,
+            [0, Wolf.SPR_PINKY_W2, 10, Wolf.AI.T_Ghosts, null, Wolf.st_chase1],// Wolf.st_chase2,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_chase3s],// Wolf.st_chase3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_chase4],     // Wolf.st_chase3s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_DEMO, 10, null, null, Wolf.st_die2],// Wolf.st_die1,
+            [0, Wolf.SPR_DEMO, 10, null, null, Wolf.st_die3],// Wolf.st_die2,
+            [0, Wolf.SPR_DEMO, 10, null, null, Wolf.st_dead],// Wolf.st_die3,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die4,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die5,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die6,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die9,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_dead] // Wolf.st_dead
+        ],
+        // en_inky,
+        [
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_stand], // Wolf.st_stand,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1s],// Wolf.st_path1,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path2], // Wolf.st_path1s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3], // Wolf.st_path2,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3s],// Wolf.st_path3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path4], // Wolf.st_path3s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1], // Wolf.st_path4,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot2],// Wolf.st_shoot1,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot3],// Wolf.st_shoot2,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot4],// Wolf.st_shoot3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot5],// Wolf.st_shoot4,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_shoot6],// Wolf.st_shoot5,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_chase1],// Wolf.st_shoot6,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_shoot8],// Wolf.st_shoot7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_shoot9],// Wolf.st_shoot8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_shoot9,
+
+            [0, Wolf.SPR_INKY_W1, 10, Wolf.AI.T_Ghosts, null, Wolf.st_chase2],// Wolf.st_chase1,
+            [0, Wolf.SPR_DEMO,  0, null, null, Wolf.st_chase2],            // Wolf.st_chase1s,
+            [0, Wolf.SPR_INKY_W2, 10, Wolf.AI.T_Ghosts, null, Wolf.st_chase1],// Wolf.st_chase2,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_chase3s],// Wolf.st_chase3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_chase4],     // Wolf.st_chase3s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_DEMO, 10, null, null, Wolf.st_die2],// Wolf.st_die1,
+            [0, Wolf.SPR_DEMO, 10, null, null, Wolf.st_die3],// Wolf.st_die2,
+            [0, Wolf.SPR_DEMO, 10, null, null, Wolf.st_dead],// Wolf.st_die3,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die4,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die5,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die6,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die9,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_dead] // Wolf.st_dead
+        ],
+        // en_gretel,
+        [
+            [0, Wolf.SPR_GRETEL_W1,    0, Wolf.AI.T_Stand, null, Wolf.st_stand], // Wolf.st_stand,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1s],// Wolf.st_path1,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path2], // Wolf.st_path1s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3], // Wolf.st_path2,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3s],// Wolf.st_path3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path4], // Wolf.st_path3s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1], // Wolf.st_path4,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [0, Wolf.SPR_GRETEL_SHOOT1, 30, null, null,        Wolf.st_shoot2],// Wolf.st_shoot1,
+            [0, Wolf.SPR_GRETEL_SHOOT2, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot3],// Wolf.st_shoot2,
+            [0, Wolf.SPR_GRETEL_SHOOT3, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot4],// Wolf.st_shoot3,
+            [0, Wolf.SPR_GRETEL_SHOOT2, 10, null, Wolf.AI.T_Shoot,    Wolf.st_shoot5],// Wolf.st_shoot4,
+            [0, Wolf.SPR_GRETEL_SHOOT3, 10, null, Wolf.AI.T_Shoot,    Wolf.st_shoot6],// Wolf.st_shoot5,
+            [0, Wolf.SPR_GRETEL_SHOOT2, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot7],// Wolf.st_shoot6,
+            [0, Wolf.SPR_GRETEL_SHOOT3, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot8],// Wolf.st_shoot7,
+            [0, Wolf.SPR_GRETEL_SHOOT1, 10, null, null,        Wolf.st_chase1],// Wolf.st_shoot8,
+
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot9,
+
+            [0, Wolf.SPR_GRETEL_W1, 10, Wolf.AI.T_Chase, null, Wolf.st_chase1s],// Wolf.st_chase1,
+            [0, Wolf.SPR_GRETEL_W1,  3, null        , null, Wolf.st_chase2], // Wolf.st_chase1s,
+            [0, Wolf.SPR_GRETEL_W2,  8, Wolf.AI.T_Chase, null, Wolf.st_chase3], // Wolf.st_chase2,
+            [0, Wolf.SPR_GRETEL_W3, 10, Wolf.AI.T_Chase, null, Wolf.st_chase3s],// Wolf.st_chase3,
+            [0, Wolf.SPR_GRETEL_W3,  3, null        , null, Wolf.st_chase4],     // Wolf.st_chase3s,
+            [0, Wolf.SPR_GRETEL_W4,  8, Wolf.AI.T_Chase, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_GRETEL_DIE1, 15, null, Wolf.ActorAI.deathScream, Wolf.st_die2],// Wolf.st_die1,
+            [0, Wolf.SPR_GRETEL_DIE2, 15, null, null,                    Wolf.st_die3],// Wolf.st_die2,
+            [0, Wolf.SPR_GRETEL_DIE3, 15, null, null,                    Wolf.st_dead],// Wolf.st_die3,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die4,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die5,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die6,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die9,
+
+            [0, Wolf.SPR_GRETEL_DEAD, 0, null, null, Wolf.st_dead] // Wolf.st_dead
+        ],
+        // en_gift,
+        [
+            [0, Wolf.SPR_GIFT_W1,    0, Wolf.AI.T_Stand, null, Wolf.st_stand], // Wolf.st_stand,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1s],// Wolf.st_path1,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path2], // Wolf.st_path1s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3], // Wolf.st_path2,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3s],// Wolf.st_path3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path4], // Wolf.st_path3s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1], // Wolf.st_path4,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [0, Wolf.SPR_GIFT_SHOOT1, 30, null, null,     Wolf.st_shoot2],// Wolf.st_shoot1,
+            [0, Wolf.SPR_GIFT_SHOOT2, 10, null, Wolf.AI.T_Launch, Wolf.st_chase1],// Wolf.st_shoot2,
+            
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot3,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],//  Wolf.st_shoot4,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot5,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot6,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot7,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot8,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot9,
+
+            [0, Wolf.SPR_GIFT_W1, 10, Wolf.AI.T_BossChase, null, Wolf.st_chase1s],// Wolf.st_chase1,
+            [0, Wolf.SPR_GIFT_W1,  3, null            , null, Wolf.st_chase2], // Wolf.st_chase1s,
+            [0, Wolf.SPR_GIFT_W2,  8, Wolf.AI.T_BossChase, null, Wolf.st_chase3], // Wolf.st_chase2,
+            [0, Wolf.SPR_GIFT_W3, 10, Wolf.AI.T_BossChase, null, Wolf.st_chase3s],// Wolf.st_chase3,
+            [0, Wolf.SPR_GIFT_W3,  3, null            , null, Wolf.st_chase4],     // Wolf.st_chase3s,
+            [0, Wolf.SPR_GIFT_W4,  8, Wolf.AI.T_BossChase, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_GIFT_W1,     10, null, Wolf.ActorAI.deathScream, Wolf.st_die2],// Wolf.st_die1,
+            [0, Wolf.SPR_GIFT_W1,     10, null, null,                     Wolf.st_die3],// Wolf.st_die2,
+            [0, Wolf.SPR_GIFT_DIE1, 10, null, null,                     Wolf.st_die4],// Wolf.st_die3,
+            [0, Wolf.SPR_GIFT_DIE2, 10, null, null,                     Wolf.st_die5],// Wolf.st_die4,
+            [0, Wolf.SPR_GIFT_DIE3, 10, null, null,                     Wolf.st_dead],// Wolf.st_die5,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die6,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die9,
+
+            [0, Wolf.SPR_GIFT_DEAD, 0, null, Wolf.ActorAI.startDeathCam, Wolf.st_dead] // Wolf.st_dead
+        ],
+        // en_fat,
+        [
+            [0, Wolf.SPR_FAT_W1,    0, Wolf.AI.T_Stand, null, Wolf.st_stand], // Wolf.st_stand,
+
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1s],// Wolf.st_path1,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path2], // Wolf.st_path1s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3], // Wolf.st_path2,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path3s],// Wolf.st_path3,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path4], // Wolf.st_path3s,
+            [0, Wolf.SPR_DEMO, 0, null, null, Wolf.st_path1], // Wolf.st_path4,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_chase1],// Wolf.st_pain1,
+        
+            [0, Wolf.SPR_FAT_SHOOT1, 30, null, null,                 Wolf.st_shoot2],// Wolf.st_shoot1,
+            [0, Wolf.SPR_FAT_SHOOT2, 10, null, Wolf.AI.T_Launch, Wolf.st_shoot3],// Wolf.st_shoot2,
+            [0, Wolf.SPR_FAT_SHOOT3,    10, null,    Wolf.AI.T_Shoot, Wolf.st_shoot4],// Wolf.st_shoot3,
+            [0, Wolf.SPR_FAT_SHOOT4,    10, null,    Wolf.AI.T_Shoot, Wolf.st_shoot5],//  Wolf.st_shoot4,
+            [0, Wolf.SPR_FAT_SHOOT3,    10, null,    Wolf.AI.T_Shoot, Wolf.st_shoot6],// Wolf.st_shoot5,
+            [0, Wolf.SPR_FAT_SHOOT4,    10, null,    Wolf.AI.T_Shoot, Wolf.st_chase1],// Wolf.st_shoot6,
+
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot7,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot8,
+            [0, Wolf.SPR_DEMO,    0, null,     null, Wolf.st_chase1],// Wolf.st_shoot9,
+
+            [0, Wolf.SPR_FAT_W1, 10, Wolf.AI.T_BossChase, null, Wolf.st_chase1s],// Wolf.st_chase1,
+            [0, Wolf.SPR_FAT_W1,  3, null       , null, Wolf.st_chase2], // Wolf.st_chase1s,
+            [0, Wolf.SPR_FAT_W2,  8, Wolf.AI.T_BossChase, null, Wolf.st_chase3], // Wolf.st_chase2,
+            [0, Wolf.SPR_FAT_W3, 10, Wolf.AI.T_BossChase, null, Wolf.st_chase3s],// Wolf.st_chase3,
+            [0, Wolf.SPR_FAT_W3,  3, null       , null, Wolf.st_chase4],     // Wolf.st_chase3s,
+            [0, Wolf.SPR_FAT_W4,  8, Wolf.AI.T_BossChase, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_FAT_W1,        10, null, Wolf.ActorAI.deathScream, Wolf.st_die2],// Wolf.st_die1,
+            [0, Wolf.SPR_FAT_W1,        10, null, null,                     Wolf.st_die3],// Wolf.st_die2,
+            [0, Wolf.SPR_FAT_DIE1, 10, null, null,                     Wolf.st_die4],// Wolf.st_die3,
+            [0, Wolf.SPR_FAT_DIE2,    10, null, null,                     Wolf.st_die5],// Wolf.st_die4,
+            [0, Wolf.SPR_FAT_DIE3,    10, null, null,                     Wolf.st_dead],// Wolf.st_die5,
+
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die6,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die7,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die8,
+            [0, Wolf.SPR_DEMO,    0, null, null, Wolf.st_dead],// Wolf.st_die9,
+
+            [0, Wolf.SPR_FAT_DEAD, 0, null, Wolf.ActorAI.startDeathCam, Wolf.st_dead] // Wolf.st_dead
+        ],
+    // --- Projectiles
+        // en_needle,
+        [
+            ST_INFO_NULL, // Wolf.st_stand,
+
+            [0, Wolf.SPR_HYPO1, 6, Wolf.AI.T_Projectile, null, Wolf.st_path2], // Wolf.st_path1,
+            ST_INFO_NULL, // Wolf.st_path1s,
+            [0, Wolf.SPR_HYPO2, 6, Wolf.AI.T_Projectile, null, Wolf.st_path3], // Wolf.st_path2,
+            [0, Wolf.SPR_HYPO3, 6, Wolf.AI.T_Projectile, null, Wolf.st_path4], // Wolf.st_path3,
+            ST_INFO_NULL, // Wolf.st_path3s,
+            [0, Wolf.SPR_HYPO4, 6, Wolf.AI.T_Projectile, null, Wolf.st_path1], // Wolf.st_path4,
+
+            ST_INFO_NULL,// Wolf.st_pain,
+            ST_INFO_NULL,// Wolf.st_pain1,
+        
+            ST_INFO_NULL,// Wolf.st_shoot1,
+            ST_INFO_NULL,// Wolf.st_shoot2,
+            ST_INFO_NULL,// Wolf.st_shoot3,
+            ST_INFO_NULL,//  Wolf.st_shoot4,
+            ST_INFO_NULL,// Wolf.st_shoot5,
+            ST_INFO_NULL,// Wolf.st_shoot6,
+
+            ST_INFO_NULL,// Wolf.st_shoot7,
+            ST_INFO_NULL,// Wolf.st_shoot8,
+            ST_INFO_NULL,// Wolf.st_shoot9,
+
+            ST_INFO_NULL,// Wolf.st_chase1,
+            ST_INFO_NULL, // Wolf.st_chase1s,
+            ST_INFO_NULL, // Wolf.st_chase2,
+            ST_INFO_NULL,// Wolf.st_chase3,
+            ST_INFO_NULL,     // Wolf.st_chase3s,
+            ST_INFO_NULL, // Wolf.st_chase4,
+
+            ST_INFO_NULL, // Wolf.st_die1,
+            ST_INFO_NULL, // Wolf.st_die2,
+            ST_INFO_NULL, // Wolf.st_die3,
+            ST_INFO_NULL,// Wolf.st_die4,
+            ST_INFO_NULL,// Wolf.st_die5,
+
+            ST_INFO_NULL,// Wolf.st_die6,
+            ST_INFO_NULL,// Wolf.st_die7,
+            ST_INFO_NULL,// Wolf.st_die8,
+            ST_INFO_NULL,// Wolf.st_die9,
+
+            ST_INFO_NULL // Wolf.st_dead
+        ],
+        // en_fire,
+        [
+            ST_INFO_NULL, // Wolf.st_stand,
+
+            [0, Wolf.SPR_FIRE1, 6, null, Wolf.AI.T_Projectile, Wolf.st_path2], // Wolf.st_path1,
+            ST_INFO_NULL, // Wolf.st_path1s,
+            [0, Wolf.SPR_FIRE2, 6, null, Wolf.AI.T_Projectile, Wolf.st_path1], // Wolf.st_path2,
+            ST_INFO_NULL, // Wolf.st_path3,
+            ST_INFO_NULL, // Wolf.st_path3s,
+            ST_INFO_NULL, // Wolf.st_path4,
+
+            ST_INFO_NULL,// Wolf.st_pain,
+            ST_INFO_NULL,// Wolf.st_pain1,
+        
+            ST_INFO_NULL,// Wolf.st_shoot1,
+            ST_INFO_NULL,// Wolf.st_shoot2,
+            ST_INFO_NULL,// Wolf.st_shoot3,
+            ST_INFO_NULL,//  Wolf.st_shoot4,
+            ST_INFO_NULL,// Wolf.st_shoot5,
+            ST_INFO_NULL,// Wolf.st_shoot6,
+
+            ST_INFO_NULL,// Wolf.st_shoot7,
+            ST_INFO_NULL,// Wolf.st_shoot8,
+            ST_INFO_NULL,// Wolf.st_shoot9,
+
+            ST_INFO_NULL,// Wolf.st_chase1,
+            ST_INFO_NULL, // Wolf.st_chase1s,
+            ST_INFO_NULL, // Wolf.st_chase2,
+            ST_INFO_NULL,// Wolf.st_chase3,
+            ST_INFO_NULL,     // Wolf.st_chase3s,
+            ST_INFO_NULL, // Wolf.st_chase4,
+
+            ST_INFO_NULL, // Wolf.st_die1,
+            ST_INFO_NULL, // Wolf.st_die2,
+            ST_INFO_NULL, // Wolf.st_die3,
+            ST_INFO_NULL,// Wolf.st_die4,
+            ST_INFO_NULL,// Wolf.st_die5,
+
+            ST_INFO_NULL,// Wolf.st_die6,
+            ST_INFO_NULL,// Wolf.st_die7,
+            ST_INFO_NULL,// Wolf.st_die8,
+            ST_INFO_NULL,// Wolf.st_die9,
+
+            ST_INFO_NULL // Wolf.st_dead
+        ],
+        // en_rocket,
+        [
+            [1, Wolf.SPR_ROCKET_1, 3, Wolf.AI.T_Projectile, Wolf.ActorAI.smoke, Wolf.st_stand], // Wolf.st_stand,
+
+            ST_INFO_NULL,// Wolf.st_path1,
+            ST_INFO_NULL, // Wolf.st_path1s,
+            ST_INFO_NULL, // Wolf.st_path2,
+            ST_INFO_NULL,// Wolf.st_path3,
+            ST_INFO_NULL, // Wolf.st_path3s,
+            ST_INFO_NULL, // Wolf.st_path4,
+
+            ST_INFO_NULL,// Wolf.st_pain,
+            ST_INFO_NULL,// Wolf.st_pain1,
+        
+            ST_INFO_NULL,// Wolf.st_shoot1,
+            ST_INFO_NULL,// Wolf.st_shoot2,
+            ST_INFO_NULL,// Wolf.st_shoot3,
+            ST_INFO_NULL,//  Wolf.st_shoot4,
+            ST_INFO_NULL,// Wolf.st_shoot5,
+            ST_INFO_NULL,// Wolf.st_shoot6,
+
+            ST_INFO_NULL,// Wolf.st_shoot7,
+            ST_INFO_NULL,// Wolf.st_shoot8,
+            ST_INFO_NULL,// Wolf.st_shoot9,
+
+            ST_INFO_NULL,// Wolf.st_chase1,
+            ST_INFO_NULL, // Wolf.st_chase1s,
+            ST_INFO_NULL, // Wolf.st_chase2,
+            ST_INFO_NULL,// Wolf.st_chase3,
+            ST_INFO_NULL,     // Wolf.st_chase3s,
+            ST_INFO_NULL, // Wolf.st_chase4,
+
+            [0, Wolf.SPR_BOOM_1, 6, null, null, Wolf.st_die2], // Wolf.st_die1,
+            [0, Wolf.SPR_BOOM_2, 6, null, null, Wolf.st_die3], // Wolf.st_die2,
+            [0, Wolf.SPR_BOOM_3, 6, null, null, Wolf.st_remove], // Wolf.st_die3,
+            ST_INFO_NULL,// Wolf.st_die4,
+            ST_INFO_NULL,// Wolf.st_die5,
+
+            ST_INFO_NULL,// Wolf.st_die6,
+            ST_INFO_NULL,// Wolf.st_die7,
+            ST_INFO_NULL,// Wolf.st_die8,
+            ST_INFO_NULL,// Wolf.st_die9,
+
+            ST_INFO_NULL // Wolf.st_dead
+        ],
+        // en_smoke,
+        [
+            ST_INFO_NULL, // Wolf.st_stand,
+
+            ST_INFO_NULL, // Wolf.st_path1,
+            ST_INFO_NULL, // Wolf.st_path1s,
+            ST_INFO_NULL, // Wolf.st_path2,
+            ST_INFO_NULL, // Wolf.st_path3,
+            ST_INFO_NULL, // Wolf.st_path3s,
+            ST_INFO_NULL, // Wolf.st_path4,
+
+            ST_INFO_NULL, // Wolf.st_pain,
+            ST_INFO_NULL, // Wolf.st_pain1,
+        
+            ST_INFO_NULL, // Wolf.st_shoot1,
+            ST_INFO_NULL, // Wolf.st_shoot2,
+            ST_INFO_NULL, // Wolf.st_shoot3,
+            ST_INFO_NULL, // Wolf.st_shoot4,
+            ST_INFO_NULL, // Wolf.st_shoot5,
+            ST_INFO_NULL, // Wolf.st_shoot6,
+
+            ST_INFO_NULL, // Wolf.st_shoot7,
+            ST_INFO_NULL, // Wolf.st_shoot8,
+            ST_INFO_NULL, // Wolf.st_shoot9,
+
+            ST_INFO_NULL, // Wolf.st_chase1,
+            ST_INFO_NULL, // Wolf.st_chase1s,
+            ST_INFO_NULL, // Wolf.st_chase2,
+            ST_INFO_NULL, // Wolf.st_chase3,
+            ST_INFO_NULL,    // Wolf.st_chase3s,
+            ST_INFO_NULL, // Wolf.st_chase4,
+
+            [0, Wolf.SPR_SMOKE_1, 3, null, null, Wolf.st_die2], // Wolf.st_die1,
+            [0, Wolf.SPR_SMOKE_2, 3, null, null, Wolf.st_die3], // Wolf.st_die2,
+            [0, Wolf.SPR_SMOKE_3, 3, null, null, Wolf.st_die4], // Wolf.st_die3,
+            [0, Wolf.SPR_SMOKE_4, 3, null, null, Wolf.st_remove], // Wolf.st_die4,
+            ST_INFO_NULL, // Wolf.st_die5,
+
+            ST_INFO_NULL, // Wolf.st_die6,
+            ST_INFO_NULL, // Wolf.st_die7,
+            ST_INFO_NULL, // Wolf.st_die8,
+            ST_INFO_NULL, // Wolf.st_die9,
+
+            ST_INFO_NULL  // Wolf.st_dead
+        ],
+        // en_bj,
+        [
+            ST_INFO_NULL, // Wolf.st_stand,
+
+            [0, Wolf.SPR_BJ_W1, 12, Wolf.AI.T_BJRun,    null, Wolf.st_path1s], // Wolf.st_path1,
+            [0, Wolf.SPR_BJ_W1,  3, null,               null, Wolf.st_path2], // Wolf.st_path1s,
+            [0, Wolf.SPR_BJ_W2,  8, Wolf.AI.T_BJRun,    null, Wolf.st_path3], // Wolf.st_path2,
+            [0, Wolf.SPR_BJ_W3, 12, Wolf.AI.T_BJRun,    null, Wolf.st_path3s], // Wolf.st_path3,
+            [0, Wolf.SPR_BJ_W3,  3, null,               null, Wolf.st_path4], // Wolf.st_path3s,
+            [0, Wolf.SPR_BJ_W4,  8, Wolf.AI.T_BJRun,    null, Wolf.st_path1], // Wolf.st_path4,
+
+            ST_INFO_NULL, // Wolf.st_pain,
+            ST_INFO_NULL, // Wolf.st_pain1,
+        
+            [0, Wolf.SPR_BJ_JUMP1, 14, Wolf.AI.T_BJJump,    null,               Wolf.st_shoot2], // Wolf.st_shoot1,
+            [0, Wolf.SPR_BJ_JUMP2, 14, Wolf.AI.T_BJJump,    Wolf.AI.T_BJYell,   Wolf.st_shoot3], // Wolf.st_shoot2,
+            [0, Wolf.SPR_BJ_JUMP3, 14, Wolf.AI.T_BJJump,    null,               Wolf.st_shoot4], // Wolf.st_shoot3,
+            [0, Wolf.SPR_BJ_JUMP4,150, null,                Wolf.AI.T_BJDone,   Wolf.st_shoot4], // Wolf.st_shoot4,
+            ST_INFO_NULL, // Wolf.st_shoot5,
+            ST_INFO_NULL, // Wolf.st_shoot6,
+
+            ST_INFO_NULL, // Wolf.st_shoot7,
+            ST_INFO_NULL, // Wolf.st_shoot8,
+            ST_INFO_NULL, // Wolf.st_shoot9,
+
+            ST_INFO_NULL, // Wolf.st_chase1,
+            ST_INFO_NULL, // Wolf.st_chase1s,
+            ST_INFO_NULL, // Wolf.st_chase2,
+            ST_INFO_NULL, // Wolf.st_chase3,
+            ST_INFO_NULL,    // Wolf.st_chase3s,
+            ST_INFO_NULL, // Wolf.st_chase4,
+
+            ST_INFO_NULL, // Wolf.st_die1,
+            ST_INFO_NULL, // Wolf.st_die2,
+            ST_INFO_NULL, // Wolf.st_die3,
+            ST_INFO_NULL, // Wolf.st_die4,
+            ST_INFO_NULL, // Wolf.st_die5,
+
+            ST_INFO_NULL, // Wolf.st_die6,
+            ST_INFO_NULL, // Wolf.st_die7,
+            ST_INFO_NULL, // Wolf.st_die8,
+            ST_INFO_NULL, // Wolf.st_die9,
+
+            ST_INFO_NULL  // Wolf.st_dead
+        ],
+
+    // --- Spear of destiny!
+        // en_spark,
+        [
+            ST_INFO_NULL, // Wolf.st_stand,
+
+            [0, Wolf.SPR_SPARK1, 6, Wolf.AI.T_Projectile, null, Wolf.st_path2], // Wolf.st_path1,
+            ST_INFO_NULL, // Wolf.st_path1s,
+            [0, Wolf.SPR_SPARK2, 6, Wolf.AI.T_Projectile, null, Wolf.st_path3], // Wolf.st_path2,
+            [0, Wolf.SPR_SPARK3, 6, Wolf.AI.T_Projectile, null, Wolf.st_path4], // Wolf.st_path3,
+            ST_INFO_NULL, // Wolf.st_path3s,
+            [0, Wolf.SPR_SPARK4, 6, Wolf.AI.T_Projectile, null, Wolf.st_path1], // Wolf.st_path4,
+
+            ST_INFO_NULL,// Wolf.st_pain,
+            ST_INFO_NULL,// Wolf.st_pain1,
+        
+            ST_INFO_NULL,// Wolf.st_shoot1,
+            ST_INFO_NULL,// Wolf.st_shoot2,
+            ST_INFO_NULL,// Wolf.st_shoot3,
+            ST_INFO_NULL,//  Wolf.st_shoot4,
+            ST_INFO_NULL,// Wolf.st_shoot5,
+            ST_INFO_NULL,// Wolf.st_shoot6,
+
+            ST_INFO_NULL,// Wolf.st_shoot7,
+            ST_INFO_NULL,// Wolf.st_shoot8,
+            ST_INFO_NULL,// Wolf.st_shoot9,
+
+            ST_INFO_NULL,// Wolf.st_chase1,
+            ST_INFO_NULL, // Wolf.st_chase1s,
+            ST_INFO_NULL, // Wolf.st_chase2,
+            ST_INFO_NULL,// Wolf.st_chase3,
+            ST_INFO_NULL,     // Wolf.st_chase3s,
+            ST_INFO_NULL, // Wolf.st_chase4,
+
+            ST_INFO_NULL, // Wolf.st_die1,
+            ST_INFO_NULL, // Wolf.st_die2,
+            ST_INFO_NULL, // Wolf.st_die3,
+            ST_INFO_NULL,// Wolf.st_die4,
+            ST_INFO_NULL,// Wolf.st_die5,
+
+            ST_INFO_NULL,// Wolf.st_die6,
+            ST_INFO_NULL,// Wolf.st_die7,
+            ST_INFO_NULL,// Wolf.st_die8,
+            ST_INFO_NULL,// Wolf.st_die9,
+
+            ST_INFO_NULL // Wolf.st_dead
+        ],
+        // en_hrocket,
+        [
+            [1, Wolf.SPR_HROCKET_1, 3, Wolf.AI.T_Projectile, Wolf.ActorAI.smoke, Wolf.st_stand], // Wolf.st_stand,
+
+            ST_INFO_NULL,// Wolf.st_path1,
+            ST_INFO_NULL, // Wolf.st_path1s,
+            ST_INFO_NULL, // Wolf.st_path2,
+            ST_INFO_NULL,// Wolf.st_path3,
+            ST_INFO_NULL, // Wolf.st_path3s,
+            ST_INFO_NULL, // Wolf.st_path4,
+
+            ST_INFO_NULL,// Wolf.st_pain,
+            ST_INFO_NULL,// Wolf.st_pain1,
+        
+            ST_INFO_NULL,// Wolf.st_shoot1,
+            ST_INFO_NULL,// Wolf.st_shoot2,
+            ST_INFO_NULL,// Wolf.st_shoot3,
+            ST_INFO_NULL,//  Wolf.st_shoot4,
+            ST_INFO_NULL,// Wolf.st_shoot5,
+            ST_INFO_NULL,// Wolf.st_shoot6,
+
+            ST_INFO_NULL,// Wolf.st_shoot7,
+            ST_INFO_NULL,// Wolf.st_shoot8,
+            ST_INFO_NULL,// Wolf.st_shoot9,
+
+            ST_INFO_NULL,// Wolf.st_chase1,
+            ST_INFO_NULL, // Wolf.st_chase1s,
+            ST_INFO_NULL, // Wolf.st_chase2,
+            ST_INFO_NULL,// Wolf.st_chase3,
+            ST_INFO_NULL,     // Wolf.st_chase3s,
+            ST_INFO_NULL, // Wolf.st_chase4,
+
+            [0, Wolf.SPR_HBOOM_1, 6, null, null, Wolf.st_die2], // Wolf.st_die1,
+            [0, Wolf.SPR_HBOOM_2, 6, null, null, Wolf.st_die3], // Wolf.st_die2,
+            [0, Wolf.SPR_HBOOM_3, 6, null, null, Wolf.st_remove], // Wolf.st_die3,
+            ST_INFO_NULL,// Wolf.st_die4,
+            ST_INFO_NULL,// Wolf.st_die5,
+
+            ST_INFO_NULL,// Wolf.st_die6,
+            ST_INFO_NULL,// Wolf.st_die7,
+            ST_INFO_NULL,// Wolf.st_die8,
+            ST_INFO_NULL,// Wolf.st_die9,
+
+            ST_INFO_NULL // Wolf.st_dead
+        ],
+        // en_hsmoke,
+        [
+            ST_INFO_NULL, // Wolf.st_stand,
+
+            ST_INFO_NULL, // Wolf.st_path1,
+            ST_INFO_NULL, // Wolf.st_path1s,
+            ST_INFO_NULL, // Wolf.st_path2,
+            ST_INFO_NULL, // Wolf.st_path3,
+            ST_INFO_NULL, // Wolf.st_path3s,
+            ST_INFO_NULL, // Wolf.st_path4,
+
+            ST_INFO_NULL, // Wolf.st_pain,
+            ST_INFO_NULL, // Wolf.st_pain1,
+        
+            ST_INFO_NULL, // Wolf.st_shoot1,
+            ST_INFO_NULL, // Wolf.st_shoot2,
+            ST_INFO_NULL, // Wolf.st_shoot3,
+            ST_INFO_NULL, // Wolf.st_shoot4,
+            ST_INFO_NULL, // Wolf.st_shoot5,
+            ST_INFO_NULL, // Wolf.st_shoot6,
+
+            ST_INFO_NULL, // Wolf.st_shoot7,
+            ST_INFO_NULL, // Wolf.st_shoot8,
+            ST_INFO_NULL, // Wolf.st_shoot9,
+
+            ST_INFO_NULL, // Wolf.st_chase1,
+            ST_INFO_NULL, // Wolf.st_chase1s,
+            ST_INFO_NULL, // Wolf.st_chase2,
+            ST_INFO_NULL, // Wolf.st_chase3,
+            ST_INFO_NULL,    // Wolf.st_chase3s,
+            ST_INFO_NULL, // Wolf.st_chase4,
+
+            [0, Wolf.SPR_HSMOKE_1, 3, null, null, Wolf.st_die2], // Wolf.st_die1,
+            [0, Wolf.SPR_HSMOKE_2, 3, null, null, Wolf.st_die3], // Wolf.st_die2,
+            [0, Wolf.SPR_HSMOKE_3, 3, null, null, Wolf.st_die4], // Wolf.st_die3,
+            [0, Wolf.SPR_HSMOKE_4, 3, null, null, Wolf.st_remove], // Wolf.st_die4,
+            ST_INFO_NULL, // Wolf.st_die5,
+
+            ST_INFO_NULL, // Wolf.st_die6,
+            ST_INFO_NULL, // Wolf.st_die7,
+            ST_INFO_NULL, // Wolf.st_die8,
+            ST_INFO_NULL, // Wolf.st_die9,
+
+            ST_INFO_NULL  // Wolf.st_dead
+        ],
+        // en_spectre,
+        [
+            ST_INFO_NULL, // Wolf.st_stand,
+
+            [0, Wolf.SPR_SPECTRE_W1, 10, Wolf.AI.T_Stand, null, Wolf.st_path2], // Wolf.st_path1,
+            ST_INFO_NULL, // Wolf.st_path1s,
+            [0, Wolf.SPR_SPECTRE_W2, 10, Wolf.AI.T_Stand, null, Wolf.st_path3], // Wolf.st_path2,
+            [0, Wolf.SPR_SPECTRE_W3, 10, Wolf.AI.T_Stand, null, Wolf.st_path4], // Wolf.st_path3,
+            ST_INFO_NULL, // Wolf.st_path3s,
+            [0, Wolf.SPR_SPECTRE_W4, 10, Wolf.AI.T_Stand, null, Wolf.st_path1], // Wolf.st_path4,
+
+            ST_INFO_NULL, // Wolf.st_pain,
+            ST_INFO_NULL, // Wolf.st_pain1,
+        
+            ST_INFO_NULL, // Wolf.st_shoot1,
+            ST_INFO_NULL, // Wolf.st_shoot2,
+            ST_INFO_NULL, // Wolf.st_shoot3,
+            ST_INFO_NULL, // Wolf.st_shoot4,
+            ST_INFO_NULL, // Wolf.st_shoot5,
+            ST_INFO_NULL, // Wolf.st_shoot6,
+
+            ST_INFO_NULL, // Wolf.st_shoot7,
+            ST_INFO_NULL, // Wolf.st_shoot8,
+            ST_INFO_NULL, // Wolf.st_shoot9,
+
+            [0, Wolf.SPR_SPECTRE_W1, 10, Wolf.AI.T_Ghosts, null, Wolf.st_chase2], // Wolf.st_chase1,
+            ST_INFO_NULL, // Wolf.st_chase1s,
+            [0, Wolf.SPR_SPECTRE_W2, 10, Wolf.AI.T_Ghosts, null, Wolf.st_chase3], // Wolf.st_chase2,
+            [0, Wolf.SPR_SPECTRE_W3, 10, Wolf.AI.T_Ghosts, null, Wolf.st_chase4], // Wolf.st_chase3,
+            ST_INFO_NULL,    // Wolf.st_chase3s,
+            [0, Wolf.SPR_SPECTRE_W4, 10, Wolf.AI.T_Ghosts, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_SPECTRE_F1, 10, null, null, Wolf.st_die2], // Wolf.st_die1,
+            [0, Wolf.SPR_SPECTRE_F2, 10, null, null, Wolf.st_die3], // Wolf.st_die2,
+            [0, Wolf.SPR_SPECTRE_F3, 10, null, null, Wolf.st_die4], // Wolf.st_die3,
+            [0, Wolf.SPR_SPECTRE_F4, 300, null, null, Wolf.st_die5], // Wolf.st_die4,
+            [0, Wolf.SPR_SPECTRE_F4, 10, null, Wolf.ActorAI.dormant, Wolf.st_die5], // Wolf.st_die5,
+
+            ST_INFO_NULL, // Wolf.st_die6,
+            ST_INFO_NULL, // Wolf.st_die7,
+            ST_INFO_NULL, // Wolf.st_die8,
+            ST_INFO_NULL, // Wolf.st_die9,
+
+            ST_INFO_NULL  // Wolf.st_dead
+        ],
+        // en_angel,
+        [
+            [0, Wolf.SPR_ANGEL_W1, 0, Wolf.AI.T_Stand, null, Wolf.st_stand], // Wolf.st_stand,
+
+            ST_INFO_NULL, // Wolf.st_path1,
+            ST_INFO_NULL, // Wolf.st_path1s,
+            ST_INFO_NULL, // Wolf.st_path2,
+            ST_INFO_NULL, // Wolf.st_path3,
+            ST_INFO_NULL, // Wolf.st_path3s,
+            ST_INFO_NULL, // Wolf.st_path4,
+
+            [0, Wolf.SPR_ANGEL_TIRED1, 40, null,    Wolf.ActorAI.breathing, Wolf.st_pain1], // Wolf.st_pain,
+            [0, Wolf.SPR_ANGEL_TIRED2, 40, null,    null, Wolf.st_shoot4], // Wolf.st_pain1,
+        
+            [0, Wolf.SPR_ANGEL_SHOOT1, 10, null,    Wolf.ActorAI.startAttack, Wolf.st_shoot2], // Wolf.st_shoot1,
+            [0, Wolf.SPR_ANGEL_SHOOT2, 20, null,    Wolf.AI.T_Launch, Wolf.st_shoot3], // Wolf.st_shoot2,
+            [0, Wolf.SPR_ANGEL_SHOOT1, 10, null,    Wolf.ActorAI.relaunch, Wolf.st_shoot2], // Wolf.st_shoot3,
+
+            [0, Wolf.SPR_ANGEL_TIRED1, 40, null,    Wolf.ActorAI.breathing, Wolf.st_shoot5], // Wolf.st_shoot4,
+            [0, Wolf.SPR_ANGEL_TIRED2, 40, null,    null, Wolf.st_shoot6], // Wolf.st_shoot5,
+            [0, Wolf.SPR_ANGEL_TIRED1, 40, null,    Wolf.ActorAI.breathing, Wolf.st_shoot7], // Wolf.st_shoot6,
+            [0, Wolf.SPR_ANGEL_TIRED2, 40, null,    null, Wolf.st_shoot8], // Wolf.st_shoot7,
+            [0, Wolf.SPR_ANGEL_TIRED1, 40, null,    Wolf.ActorAI.breathing, Wolf.st_chase1], // Wolf.st_shoot8,
+            ST_INFO_NULL, // Wolf.st_shoot9,
+
+            [0, Wolf.SPR_ANGEL_W1, 10, Wolf.AI.T_BossChase,    null, Wolf.st_chase1s], // Wolf.st_chase1,
+            [0, Wolf.SPR_ANGEL_W1,  3, null,        null, Wolf.st_chase2], // Wolf.st_chase1s,
+            [0, Wolf.SPR_ANGEL_W2,  8, Wolf.AI.T_BossChase,    null, Wolf.st_chase3], // Wolf.st_chase2,
+            [0, Wolf.SPR_ANGEL_W3, 10, Wolf.AI.T_BossChase,    null, Wolf.st_chase3s], // Wolf.st_chase3,
+            [0, Wolf.SPR_ANGEL_W3,  3, null,        null, Wolf.st_chase4],    // Wolf.st_chase3s,
+            [0, Wolf.SPR_ANGEL_W4,  8, Wolf.AI.T_BossChase,    null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_ANGEL_W1, 1, null, Wolf.ActorAI.deathScream, Wolf.st_die2], // Wolf.st_die1,
+            [0, Wolf.SPR_ANGEL_W1, 1, null, null, Wolf.st_die3], // Wolf.st_die2,
+            [0, Wolf.SPR_ANGEL_DIE1, 10, null, Wolf.ActorAI.slurpie, Wolf.st_die4], // Wolf.st_die3,
+            [0, Wolf.SPR_ANGEL_DIE2, 10, null, null, Wolf.st_die5], // Wolf.st_die4,
+            [0, Wolf.SPR_ANGEL_DIE3, 10, null, null, Wolf.st_die6], // Wolf.st_die5,
+            [0, Wolf.SPR_ANGEL_DIE4, 10, null, null, Wolf.st_die7], // Wolf.st_die6,
+            [0, Wolf.SPR_ANGEL_DIE5, 10, null, null, Wolf.st_die8], // Wolf.st_die7,
+            [0, Wolf.SPR_ANGEL_DIE6, 10, null, null, Wolf.st_die9], // Wolf.st_die8,
+            [0, Wolf.SPR_ANGEL_DIE7, 10, null, null, Wolf.st_dead], // Wolf.st_die9,
+
+            [0, Wolf.SPR_ANGEL_DEAD, 130, null, Wolf.ActorAI.victory, Wolf.st_dead]  // Wolf.st_dead
+        ],
+        // en_trans,
+        [
+            [0, Wolf.SPR_TRANS_W1, 0, Wolf.AI.T_Stand, null, Wolf.st_stand], // Wolf.st_stand,
+
+            ST_INFO_NULL, // Wolf.st_path1,
+            ST_INFO_NULL, // Wolf.st_path1s,
+            ST_INFO_NULL, // Wolf.st_path2,
+            ST_INFO_NULL, // Wolf.st_path3,
+            ST_INFO_NULL, // Wolf.st_path3s,
+            ST_INFO_NULL, // Wolf.st_path4,
+
+            ST_INFO_NULL, // Wolf.st_pain,
+            ST_INFO_NULL, // Wolf.st_pain1,
+        
+            [0, Wolf.SPR_TRANS_SHOOT1, 30, null,    null, Wolf.st_shoot2], // Wolf.st_shoot1,
+            [0, Wolf.SPR_TRANS_SHOOT2, 10, null,    Wolf.AI.T_Shoot, Wolf.st_shoot3], // Wolf.st_shoot2,
+            [0, Wolf.SPR_TRANS_SHOOT3, 10, null,    Wolf.AI.T_Shoot, Wolf.st_shoot4], // Wolf.st_shoot3,
+            [0, Wolf.SPR_TRANS_SHOOT2, 10, null,    Wolf.AI.T_Shoot, Wolf.st_shoot5], // Wolf.st_shoot4,
+            [0, Wolf.SPR_TRANS_SHOOT3, 10, null,    Wolf.AI.T_Shoot, Wolf.st_shoot6], // Wolf.st_shoot5,
+            [0, Wolf.SPR_TRANS_SHOOT2, 10, null,    Wolf.AI.T_Shoot, Wolf.st_shoot7], // Wolf.st_shoot6,
+            [0, Wolf.SPR_TRANS_SHOOT3, 10, null,    Wolf.AI.T_Shoot, Wolf.st_shoot8], // Wolf.st_shoot7,
+            [0, Wolf.SPR_TRANS_SHOOT1, 10, null,    null, Wolf.st_chase1], // Wolf.st_shoot8,
+            ST_INFO_NULL, // Wolf.st_shoot9,
+
+            [0, Wolf.SPR_TRANS_W1, 10, Wolf.AI.T_Chase, null, Wolf.st_chase1s], // Wolf.st_chase1,
+            [0, Wolf.SPR_TRANS_W1,  3, null,         null, Wolf.st_chase2], // Wolf.st_chase1s,
+            [0, Wolf.SPR_TRANS_W2,  8, Wolf.AI.T_Chase, null, Wolf.st_chase3], // Wolf.st_chase2,
+            [0, Wolf.SPR_TRANS_W3, 10, Wolf.AI.T_Chase, null, Wolf.st_chase3s], // Wolf.st_chase3,
+            [0, Wolf.SPR_TRANS_W3,  3, null,         null, Wolf.st_chase4],    // Wolf.st_chase3s,
+            [0, Wolf.SPR_TRANS_W4,  8, Wolf.AI.T_Chase, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_TRANS_W1, 1, null, Wolf.ActorAI.deathScream, Wolf.st_die2], // Wolf.st_die1,
+            [0, Wolf.SPR_TRANS_W1, 1, null, null, Wolf.st_die3], // Wolf.st_die2,
+            [0, Wolf.SPR_TRANS_DIE1, 15, null, null, Wolf.st_die4], // Wolf.st_die3,
+            [0, Wolf.SPR_TRANS_DIE2, 15, null, null, Wolf.st_die5], // Wolf.st_die4,
+            [0, Wolf.SPR_TRANS_DIE3, 15, null, null, Wolf.st_dead], // Wolf.st_die5,
+            ST_INFO_NULL, // Wolf.st_die6,
+            ST_INFO_NULL, // Wolf.st_die7,
+            ST_INFO_NULL, // Wolf.st_die8,
+            ST_INFO_NULL, // Wolf.st_die9,
+
+            [0, Wolf.SPR_TRANS_DEAD, 0, null, null, Wolf.st_dead]  // Wolf.st_dead
+        ],
+        // en_uber,
+        [
+            [0, Wolf.SPR_UBER_W1, 0, Wolf.AI.T_Stand, null, Wolf.st_stand], // Wolf.st_stand,
+
+            ST_INFO_NULL, // Wolf.st_path1,
+            ST_INFO_NULL, // Wolf.st_path1s,
+            ST_INFO_NULL, // Wolf.st_path2,
+            ST_INFO_NULL, // Wolf.st_path3,
+            ST_INFO_NULL, // Wolf.st_path3s,
+            ST_INFO_NULL, // Wolf.st_path4,
+
+            ST_INFO_NULL, // Wolf.st_pain,
+            ST_INFO_NULL, // Wolf.st_pain1,
+        
+            [0, Wolf.SPR_UBER_SHOOT1, 30, null, null, Wolf.st_shoot2], // Wolf.st_shoot1,
+            [0, Wolf.SPR_UBER_SHOOT2, 12, null, Wolf.AI.T_UShoot, Wolf.st_shoot3], // Wolf.st_shoot2,
+            [0, Wolf.SPR_UBER_SHOOT3, 12, null, Wolf.AI.T_UShoot, Wolf.st_shoot4], // Wolf.st_shoot3,
+            [0, Wolf.SPR_UBER_SHOOT4, 12, null, Wolf.AI.T_UShoot, Wolf.st_shoot5], // Wolf.st_shoot4,
+            [0, Wolf.SPR_UBER_SHOOT3, 12, null, Wolf.AI.T_UShoot, Wolf.st_shoot6], // Wolf.st_shoot5,
+            [0, Wolf.SPR_UBER_SHOOT2, 12, null, Wolf.AI.T_UShoot, Wolf.st_shoot7], // Wolf.st_shoot6,
+            [0, Wolf.SPR_UBER_SHOOT1, 12, null, null, Wolf.st_chase1], // Wolf.st_shoot7,
+            ST_INFO_NULL, // Wolf.st_shoot8,
+            ST_INFO_NULL, // Wolf.st_shoot9,
+
+            [0, Wolf.SPR_UBER_W1, 10, Wolf.AI.T_Chase, null, Wolf.st_chase1s], // Wolf.st_chase1,
+            [0, Wolf.SPR_UBER_W1,  3, null,         null, Wolf.st_chase2], // Wolf.st_chase1s,
+            [0, Wolf.SPR_UBER_W2,  8, Wolf.AI.T_Chase, null, Wolf.st_chase3], // Wolf.st_chase2,
+            [0, Wolf.SPR_UBER_W3, 10, Wolf.AI.T_Chase, null, Wolf.st_chase3s], // Wolf.st_chase3,
+            [0, Wolf.SPR_UBER_W3,  3, null,         null, Wolf.st_chase4],    // Wolf.st_chase3s,
+            [0, Wolf.SPR_UBER_W4,  8, Wolf.AI.T_Chase, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_UBER_W1, 1, null, Wolf.ActorAI.deathScream, Wolf.st_die2], // Wolf.st_die1,
+            [0, Wolf.SPR_UBER_W1, 1, null, null, Wolf.st_die3], // Wolf.st_die2,
+            [0, Wolf.SPR_UBER_DIE1, 15, null, null, Wolf.st_die4], // Wolf.st_die3,
+            [0, Wolf.SPR_UBER_DIE2, 15, null, null, Wolf.st_die5], // Wolf.st_die4,
+            [0, Wolf.SPR_UBER_DIE3, 15, null, null, Wolf.st_die6], // Wolf.st_die5,
+            [0, Wolf.SPR_UBER_DIE4, 15, null, null, Wolf.st_dead], // Wolf.st_die6,
+            ST_INFO_NULL, // Wolf.st_die7,
+            ST_INFO_NULL, // Wolf.st_die8,
+            ST_INFO_NULL, // Wolf.st_die9,
+
+            [0, Wolf.SPR_UBER_DEAD, 0, null, null, Wolf.st_dead]  // Wolf.st_dead
+        ],
+        // en_will,
+        [
+            [0, Wolf.SPR_WILL_W1, 0, Wolf.AI.T_Stand, null, Wolf.st_stand], // Wolf.st_stand,
+
+            ST_INFO_NULL, // Wolf.st_path1,
+            ST_INFO_NULL, // Wolf.st_path1s,
+            ST_INFO_NULL, // Wolf.st_path2,
+            ST_INFO_NULL, // Wolf.st_path3,
+            ST_INFO_NULL, // Wolf.st_path3s,
+            ST_INFO_NULL, // Wolf.st_path4,
+
+            ST_INFO_NULL, // Wolf.st_pain,
+            ST_INFO_NULL, // Wolf.st_pain1,
+        
+            [0, Wolf.SPR_WILL_SHOOT1, 30, null, null, Wolf.st_shoot2], // Wolf.st_shoot1,
+            [0, Wolf.SPR_WILL_SHOOT2, 10, null, Wolf.AI.T_Launch, Wolf.st_shoot3], // Wolf.st_shoot2,
+            [0, Wolf.SPR_WILL_SHOOT3, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot4], // Wolf.st_shoot3,
+            [0, Wolf.SPR_WILL_SHOOT4, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot5], // Wolf.st_shoot4,
+            [0, Wolf.SPR_WILL_SHOOT3, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot6], // Wolf.st_shoot5,
+            [0, Wolf.SPR_WILL_SHOOT4, 10, null, Wolf.AI.T_Shoot, Wolf.st_chase1], // Wolf.st_shoot6,
+            ST_INFO_NULL, // Wolf.st_shoot7,
+            ST_INFO_NULL, // Wolf.st_shoot8,
+            ST_INFO_NULL, // Wolf.st_shoot9,
+
+            [0, Wolf.SPR_WILL_W1, 10, Wolf.AI.T_BossChase, null, Wolf.st_chase1s], // Wolf.st_chase1,
+            [0, Wolf.SPR_WILL_W1,  3, null,     null, Wolf.st_chase2], // Wolf.st_chase1s,
+            [0, Wolf.SPR_WILL_W2,  8, Wolf.AI.T_BossChase, null, Wolf.st_chase3], // Wolf.st_chase2,
+            [0, Wolf.SPR_WILL_W3, 10, Wolf.AI.T_BossChase, null, Wolf.st_chase3s], // Wolf.st_chase3,
+            [0, Wolf.SPR_WILL_W3,  3, null,     null, Wolf.st_chase4],    // Wolf.st_chase3s,
+            [0, Wolf.SPR_WILL_W4,  8, Wolf.AI.T_BossChase, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_WILL_W1, 1, null, Wolf.ActorAI.deathScream, Wolf.st_die2], // Wolf.st_die1,
+            [0, Wolf.SPR_WILL_W1, 10, null, null, Wolf.st_die3], // Wolf.st_die2,
+            [0, Wolf.SPR_WILL_DIE1, 10, null, null, Wolf.st_die4], // Wolf.st_die3,
+            [0, Wolf.SPR_WILL_DIE2, 10, null, null, Wolf.st_die5], // Wolf.st_die4,
+            [0, Wolf.SPR_WILL_DIE3, 10, null, null, Wolf.st_dead], // Wolf.st_die5,
+            ST_INFO_NULL, // Wolf.st_die6,
+            ST_INFO_NULL, // Wolf.st_die7,
+            ST_INFO_NULL, // Wolf.st_die8,
+            ST_INFO_NULL, // Wolf.st_die9,
+
+            [0, Wolf.SPR_WILL_DEAD, 20, null, null, Wolf.st_dead]  // Wolf.st_dead
+        ],
+        // en_death
+        [
+            [0, Wolf.SPR_DEATH_W1, 0, Wolf.AI.T_Stand, null, Wolf.st_stand], // Wolf.st_stand,
+
+            ST_INFO_NULL, // Wolf.st_path1,
+            ST_INFO_NULL, // Wolf.st_path1s,
+            ST_INFO_NULL, // Wolf.st_path2,
+            ST_INFO_NULL, // Wolf.st_path3,
+            ST_INFO_NULL, // Wolf.st_path3s,
+            ST_INFO_NULL, // Wolf.st_path4,
+
+            ST_INFO_NULL, // Wolf.st_pain,
+            ST_INFO_NULL, // Wolf.st_pain1,
+        
+            [0, Wolf.SPR_DEATH_SHOOT1, 30, null, null, Wolf.st_shoot2], // Wolf.st_shoot1,
+            [0, Wolf.SPR_DEATH_SHOOT2, 10, null, Wolf.AI.T_Launch, Wolf.st_shoot3], // Wolf.st_shoot2,
+            [0, Wolf.SPR_DEATH_SHOOT4, 10, null, Wolf.AI.T_Shoot, Wolf.st_shoot4], // Wolf.st_shoot3,
+            [0, Wolf.SPR_DEATH_SHOOT3, 10, null, Wolf.AI.T_Launch, Wolf.st_shoot5], // Wolf.st_shoot4,
+            [0, Wolf.SPR_DEATH_SHOOT4, 10, null, Wolf.AI.T_Shoot, Wolf.st_chase1], // Wolf.st_shoot5,
+            ST_INFO_NULL, // Wolf.st_shoot6,
+            ST_INFO_NULL, // Wolf.st_shoot7,
+            ST_INFO_NULL, // Wolf.st_shoot8,
+            ST_INFO_NULL, // Wolf.st_shoot9,
+
+            [0, Wolf.SPR_DEATH_W1, 10, Wolf.AI.T_BossChase, null, Wolf.st_chase1s], // Wolf.st_chase1,
+            [0, Wolf.SPR_DEATH_W1,  3, null,      null, Wolf.st_chase2], // Wolf.st_chase1s,
+            [0, Wolf.SPR_DEATH_W2,  8, Wolf.AI.T_BossChase, null, Wolf.st_chase3], // Wolf.st_chase2,
+            [0, Wolf.SPR_DEATH_W3, 10, Wolf.AI.T_BossChase, null, Wolf.st_chase3s], // Wolf.st_chase3,
+            [0, Wolf.SPR_DEATH_W3,  3, null,      null, Wolf.st_chase4],    // Wolf.st_chase3s,
+            [0, Wolf.SPR_DEATH_W4,  8, Wolf.AI.T_BossChase, null, Wolf.st_chase1], // Wolf.st_chase4,
+
+            [0, Wolf.SPR_DEATH_W1, 1, null, Wolf.ActorAI.deathScream, Wolf.st_die2], // Wolf.st_die1,
+            [0, Wolf.SPR_DEATH_W1, 10, null, null, Wolf.st_die3], // Wolf.st_die2,
+            [0, Wolf.SPR_DEATH_DIE1, 10, null, null, Wolf.st_die4], // Wolf.st_die3,
+            [0, Wolf.SPR_DEATH_DIE2, 10, null, null, Wolf.st_die5], // Wolf.st_die4,
+            [0, Wolf.SPR_DEATH_DIE3, 10, null, null, Wolf.st_die6], // Wolf.st_die5,
+            [0, Wolf.SPR_DEATH_DIE4, 10, null, null, Wolf.st_die7], // Wolf.st_die6,
+            [0, Wolf.SPR_DEATH_DIE5, 10, null, null, Wolf.st_die7], // Wolf.st_die7,
+            [0, Wolf.SPR_DEATH_DIE6, 10, null, null, Wolf.st_die7], // Wolf.st_die8,
+            ST_INFO_NULL, // Wolf.st_die9,
+
+            [0, Wolf.SPR_DEATH_DEAD, 0, null, null, Wolf.st_dead]  // Wolf.st_dead
+        ]
+    ];
+    
+    
+    // int    starthitpoints[ 4 ][ NUMENEMIES ] =
+    var starthitpoints = [
+        //
+        // BABY MODE
+        //
+        [
+            25,     // guards
+            50,     // officer
+            100,    // SS
+            1,      // dogs
+            850,    // Hans
+            850,    // Schabbs
+            200,    // fake hitler
+            800,    // mecha hitler
+            500,    // hitler
+            45,     // mutants
+            25,     // ghosts
+            25,     // ghosts
+            25,     // ghosts
+            25,     // ghosts
+
+            850,    // Gretel
+            850,    // Gift
+            850,    // Fat
+            // --- Projectiles
+            0,      // en_needle,
+            0,      // en_fire,
+            0,      // en_rocket,
+            0,      // en_smoke,
+            100,    // en_bj,
+            // --- Spear of destiny!
+            0,      // en_spark,
+            0,      // en_hrocket,
+            0,      // en_hsmoke,
+
+            5,      // en_spectre,
+            1450,   // en_angel,
+            850,    // en_trans,
+            1050,   // en_uber,
+            950,    // en_will,
+            1250    // en_death
+        ],
+        
+        //
+        // DON'T HURT ME MODE
+        //
+        [
+            25,     // guards
+            50,     // officer
+            100,    // SS
+            1,      // dogs
+            950,    // Hans
+            950,    // Schabbs
+            300,    // fake hitler
+            950,    // mecha hitler
+            700,    // hitler
+            55,     // mutants
+            25,     // ghosts
+            25,     // ghosts
+            25,     // ghosts
+            25,     // ghosts
+
+            950,    // Gretel
+            950,    // Gift
+            950,    // Fat
+            // --- Projectiles
+            0,      // en_needle,
+            0,      // en_fire,
+            0,      // en_rocket,
+            0,      // en_smoke,
+            100,    // en_bj,
+            // --- Spear of destiny!
+            0,      // en_spark,
+            0,      // en_hrocket,
+            0,      // en_hsmoke,
+
+            10,     // en_spectre,
+            1550,   // en_angel,
+            950,    // en_trans,
+            1150,   // en_uber,
+            1050,   // en_will,
+            1350    // en_death
+        ],
+        
+        //
+        // BRING 'EM ON MODE
+        //
+        [
+            25,     // guards
+            50,     // officer
+            100,    // SS
+            1,      // dogs
+
+            1050,   // Hans
+            1550,   // Schabbs
+            400,    // fake hitler
+            1050,   // mecha hitler
+            800,    // hitler
+
+            55,     // mutants
+            25,     // ghosts
+            25,     // ghosts
+            25,     // ghosts
+            25,     // ghosts
+
+            1050,   // Gretel
+            1050,   // Gift
+            1050,   // Fat
+            // --- Projectiles
+            0,      // en_needle,
+            0,      // en_fire,
+            0,      // en_rocket,
+            0,      // en_smoke,
+            100,    // en_bj,
+            // --- Spear of destiny!
+            0,      // en_spark,
+            0,      // en_hrocket,
+            0,      // en_hsmoke,
+
+            15,     // en_spectre,
+            1650,   // en_angel,
+            1050,   // en_trans,
+            1250,   // en_uber,
+            1150,   // en_will,
+            1450    // en_death
+        ],
+        
+        //
+        // DEATH INCARNATE MODE
+        //
+        [
+            25,     // guards
+            50,     // officer
+            100,    // SS
+            1,      // dogs
+
+            1200,   // Hans
+            2400,   // Schabbs
+            500,    // fake hitler
+            1200,   // mecha hitler
+            900,    // hitler
+
+            65,     // mutants
+            25,     // ghosts
+            25,     // ghosts
+            25,     // ghosts
+            25,     // ghosts
+
+            1200,   // Gretel
+            1200,   // Gift
+            1200,   // Fat
+            // --- Projectiles
+            0,      // en_needle,
+            0,      // en_fire,
+            0,      // en_rocket,
+            0,      // en_smoke,
+            100,    // en_bj,
+            // --- Spear of destiny!
+            0,      // en_spark,
+            0,      // en_hrocket,
+            0,      // en_hsmoke,
+
+            25,     // en_spectre,
+            2000,   // en_angel,
+            1200,   // en_trans,
+            1400,   // en_uber,
+            1300,   // en_will,
+            1600    // en_death
+        ]
+    ];
+    
+    /*  
+    typedef struct
+    {
+        char rotate; // 1-if object can be rotated, 0 if one sprite for every direction
+        int texture; // base object's state texture if rotation is on facing player
+        int timeout; // after how man ticks change state to .next_state
+        think_t think; // what to do every frame
+        think_t action; // what to do once per state
+        en_state next_state; // next state
+    } 
+    */
+    
+    // convert to state structs
+    for (var i=0;i<objstate.length;i++) {
+        var obj = objstate[i];
+        for (var j=0;j<obj.length;j++) {
+            var state = obj[j];
+            obj[j] = {
+                rotate      : state[0],
+                texture     : state[1],
+                timeout     : state[2],
+                think       : state[3],
+                action      : state[4],
+                next_state  : state[5]
+            };
+        }
+    }
+    
+    Wolf.setConsts({
+        objstate : objstate,
+        starthitpoints : starthitpoints
+    });
+
+})();
\ No newline at end of file
diff --git a/js/ai.js b/js/ai.js
new file mode 100644 (file)
index 0000000..234bf15
--- /dev/null
+++ b/js/ai.js
@@ -0,0 +1,1206 @@
+/*
+* ===========================================================================
+* 
+* Wolf3D Browser Version GPL Source Code
+* Copyright (C) 2012 id Software LLC, a ZeniMax Media company. 
+* 
+* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").  
+* 
+* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+* 
+* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+* 
+* You should have received a copy of the GNU General Public License version 2
+* along with Wolf3D Browser Source Code.  If not, see <http://www.gnu.org/licenses/>.
+* 
+* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
+* 
+* ===========================================================================
+*/
+
+/** 
+ * @namespace 
+ * @description Enemy AI
+ */
+Wolf.AI = (function() {
+
+    Wolf.setConsts({
+        RUNSPEED    : 6000,
+        MINSIGHT    : 0x18000
+    });
+
+
+    function checkSight(self, game) {
+        var level = game.level,
+            player = game.player,
+            deltax, deltay;
+
+
+        // don't bother tracing a line if the area isn't connected to the player's
+        if (!(self.flags & Wolf.FL_AMBUSH)) {
+            if (!level.state.areabyplayer[self.areanumber]) {
+                return false;
+            }
+        }
+
+        // if the player is real close, sight is automatic
+        deltax = player.position.x - self.x;
+        deltay = player.position.y - self.y;
+
+        if (Math.abs(deltax) < Wolf.MINSIGHT && Math.abs(deltay) < Wolf.MINSIGHT) {
+            return true;
+        }
+
+        // see if they are looking in the right direction
+        switch (self.dir) {
+            case Wolf.Math.dir8_north:
+               if (deltay < 0) {
+                    return false;
+                }
+                break;
+            case Wolf.Math.dir8_east:
+                if (deltax < 0) {
+                    return false;
+                }
+                break;
+            case Wolf.Math.dir8_south:
+                if (deltay > 0) {
+                    return false;
+                }
+                break;
+            case Wolf.Math.dir8_west:
+                if (deltax > 0) {
+                    return false;
+                }
+                break;
+            default:
+                break;
+        }
+
+        // trace a line to check for blocking tiles (corners)
+        return Wolf.Level.checkLine(self.x, self.y, player.position.x, player.position.y, level);
+    }
+
+
+    /**
+     * @description Entity is going to move in a new direction.
+     *              Called, when actor finished previous moving & located in 
+     *              the 'center' of the tile. Entity will try walking in direction.
+     * @private
+     * @returns {boolean} true if direction is OK, otherwise false.
+     */
+    function changeDir(self, new_dir, level) {
+        var oldx, 
+            oldy, 
+            newx, 
+            newy, // all it tiles
+            n,
+            moveok = false;
+            
+        oldx = Wolf.POS2TILE(self.x);
+        oldy = Wolf.POS2TILE(self.y);
+        //assert( new_dir >= 0 && new_dir <= 8 );
+        newx = oldx + Wolf.Math.dx8dir[new_dir];
+        newy = oldy + Wolf.Math.dy8dir[new_dir];
+
+        if (new_dir & 0x01) { // same as %2 (diagonal dir)
+            if (level.tileMap[newx][oldy] & Wolf.SOLID_TILE ||
+                 level.tileMap[oldx][newy] & Wolf.SOLID_TILE ||
+                 level.tileMap[newx][newy] & Wolf.SOLID_TILE) {
+                return false;
+            }
+
+            for (n=0; n < level.state.numGuards; ++n) {
+                if (level.state.guards[n].state >= Wolf.st_die1) {
+                    continue;
+                }
+                if (level.state.guards[n].tile.x == newx && level.state.guards[n].tile.y == newy) {
+                    return false; // another guard in path
+                }
+                if (level.state.guards[n].tile.x == oldx && level.state.guards[n].tile.y == newy) {
+                    return false; // another guard in path
+                }
+                if (level.state.guards[n].tile.x == newx && level.state.guards[n].tile.y == oldy) {
+                    return false; // another guard in path
+                }
+            }
+        } else { // linear dir (E, N, W, S)
+            if (level.tileMap[newx][newy] & Wolf.SOLID_TILE) {
+                return false;
+            }
+            if (level.tileMap[newx][newy] & Wolf.DOOR_TILE) {
+                if (self.type == Wolf.en_fake || self.type == Wolf.en_dog) { // they can't open doors
+                    if (level.state.doorMap[newx][newy].action != Wolf.dr_open) { // path is blocked by a closed opened door
+                        return false;
+                    }
+                } else {
+                    self.waitfordoorx = newx;
+                    self.waitfordoory = newy;
+                    moveok = true;
+                }
+            }
+            if (!moveok) {
+                for (n = 0; n < level.state.numGuards; ++n) {
+                    if (level.state.guards[n].state >= Wolf.st_die1) {
+                        continue;
+                    }
+                    if (level.state.guards[n].tile.x == newx && level.state.guards[n].tile.y == newy) {
+                        return false; // another guard in path
+                    }
+                }
+            }
+        }
+
+        //moveok:
+        self.tile.x = newx;
+        self.tile.y = newy;
+
+        level.tileMap[oldx][oldy] &= ~Wolf.ACTOR_TILE; // update map status
+        level.tileMap[newx][newy] |= Wolf.ACTOR_TILE;
+
+        if (level.areas[newx][newy] > 0) {
+            // ambush tiles don't have valid area numbers (-3), so don't change the area if walking over them
+            self.areanumber = level.areas[newx][newy];
+            // assert( self.areanumber >= 0 && self.areanumber < NUMAREAS );
+        }
+
+        self.distance = Wolf.TILEGLOBAL;
+        self.dir = new_dir;
+
+        return true;
+    }
+
+    /**
+     * @description Entity is going to turn on a way point. 
+     * @private
+     */
+    function path(self, game) {
+        var level = game.level;
+        if (level.tileMap[self.x >> Wolf.TILESHIFT][self.y >> Wolf.TILESHIFT] & Wolf.WAYPOINT_TILE) {
+        
+            var tileinfo = level.tileMap[self.x >> Wolf.TILESHIFT][self.y >> Wolf.TILESHIFT];
+            
+            if (tileinfo & Wolf.TILE_IS_E_TURN) {
+                self.dir = Wolf.Math.dir8_east;
+            } else if (tileinfo & Wolf.TILE_IS_NE_TURN) {
+                self.dir = Wolf.Math.dir8_northeast;
+            } else if (tileinfo & Wolf.TILE_IS_N_TURN) {
+                self.dir = Wolf.Math.dir8_north;
+            } else if (tileinfo & Wolf.TILE_IS_NW_TURN) {
+                self.dir = Wolf.Math.dir8_northwest;
+            } else if (tileinfo & Wolf.TILE_IS_W_TURN) {
+                self.dir = Wolf.Math.dir8_west;
+            } else if (tileinfo & Wolf.TILE_IS_SW_TURN) {
+                self.dir = Wolf.Math.dir8_southwest;
+            } else if (tileinfo & Wolf.TILE_IS_S_TURN) {
+                self.dir = Wolf.Math.dir8_south;
+            } else if (tileinfo & Wolf.TILE_IS_SE_TURN) {
+                self.dir = Wolf.Math.dir8_southeast;
+            }
+        }
+
+        if (!changeDir(self, self.dir, level)) {
+            self.dir = Wolf.Math.dir8_nodir;
+        }
+    }
+
+
+    /**
+     * @description Called by entities that ARE NOT chasing the player. 
+     * @private
+     */
+    function findTarget(self, game, tics) {
+        var level = game.level,
+            player = game.player;
+
+        if (self.temp2) { // count down reaction time
+            self.temp2 -= tics;
+            if (self.temp2 > 0) {
+                return false;
+            }
+            self.temp2 = 0; // time to react
+        } else {
+        
+            // check if we can/want to see/hear player
+            if (player.flags & Wolf.FL_NOTARGET) {
+                return false; // notarget cheat
+            }
+
+            // assert( self.areanumber >= 0 && self.areanumber <    NUMAREAS );
+            if (!(self.flags & Wolf.FL_AMBUSH) && ! level.state.areabyplayer[self.areanumber]) {
+                return false;
+            }
+
+            if (!checkSight(self, game)) { // Player is visible - normal behavior
+                if (self.flags & Wolf.FL_AMBUSH || !player.madenoise) {
+                    return false;
+                }
+            }
+            self.flags &= ~Wolf.FL_AMBUSH;
+            
+            
+            // if we are here we see/hear player!!!
+            switch (self.type) {
+                case Wolf.en_guard:
+                    self.temp2 = 1 + Wolf.Random.rnd() / 4;
+                    break;
+
+                case Wolf.en_officer:
+                    self.temp2 = 2;
+                    break;
+
+                case Wolf.en_mutant:
+                    self.temp2 = 1 + Wolf.Random.rnd() / 6;
+                    break;
+
+                case Wolf.en_ss:
+                    self.temp2 = 1 + Wolf.Random.rnd() / 6;
+                    break;
+
+                case Wolf.en_dog:
+                    self.temp2 = 1 + Wolf.Random.rnd() / 8;
+                    break;
+
+                case Wolf.en_boss:
+                case Wolf.en_schabbs:
+                case Wolf.en_fake:
+                case Wolf.en_mecha:
+                case Wolf.en_hitler:
+                case Wolf.en_gretel:
+                case Wolf.en_gift:
+                case Wolf.en_fat:
+                case Wolf.en_spectre:
+                case Wolf.en_angel:
+                case Wolf.en_trans:
+                case Wolf.en_uber:
+                case Wolf.en_will:
+                case Wolf.en_death:
+                    self.temp2 = 1;
+                    break;
+            }
+
+            return false;  // we are amazed & waiting to understand what to do!
+        }
+
+        Wolf.ActorAI.firstSighting(self, game);
+
+        return true;
+    }
+
+
+
+    /**
+     * @description As dodge(), but doesn't try to dodge.  
+     * @private
+     */
+    function chase(self, game) {
+        var level = game.level,
+            player = game.player,
+            deltax, 
+            deltay,
+            d = [],
+            tdir, olddir, turnaround;
+
+        if (game.player.playstate == Wolf.ex_victory) {
+            return;
+        }
+        
+        olddir = self.dir;
+        turnaround = Wolf.Math.opposite8[olddir];
+        d[0] = d[1] = Wolf.Math.dir8_nodir;
+
+        deltax = Wolf.POS2TILE(player.position.x) - Wolf.POS2TILE(self.x);
+        deltay = Wolf.POS2TILE(player.position.y) - Wolf.POS2TILE(self.y);
+
+        if (deltax > 0) {
+            d[0] = Wolf.Math.dir8_east;
+        } else if (deltax < 0) {
+            d[0] = Wolf.Math.dir8_west;
+        }
+        
+        if (deltay > 0) {
+            d[1] = Wolf.Math.dir8_north;
+        } else if (deltay < 0) {
+            d[1] = Wolf.Math.dir8_south;
+        }
+
+        if (Math.abs(deltay) > Math.abs(deltax)) {
+            tdir = d[0];
+            d[0] = d[1];
+            d[1] = tdir;
+        } // swap d[0] & d[1]
+
+        if (d[0] == turnaround) {
+            d[0] = Wolf.Math.dir8_nodir;
+        }
+
+        if (d[1] == turnaround) {
+            d[1] = Wolf.Math.dir8_nodir;
+        }
+
+        if (d[0] != Wolf.Math.dir8_nodir) {
+            if (changeDir(self, d[0], level)) {
+                return;
+            }
+        }
+
+        if (d[1] != Wolf.Math.dir8_nodir) {
+            if (changeDir(self, d[1], level)) {
+                return;
+            }
+        }
+
+        // there is no direct path to the player, so pick another direction
+        if (olddir != Wolf.Math.dir8_nodir) {
+            if (changeDir(self, olddir, level)) {
+                return;
+            }
+        }
+
+        if (Wolf.Random.rnd() > 128) { // randomly determine direction of search
+            for (tdir = Wolf.Math.dir8_east; tdir <= Wolf.Math.dir8_south; tdir += 2) { // * Revision
+                if (tdir != turnaround) {
+                    if (changeDir(self, tdir, level)) {
+                        return;
+                    }
+                }
+            }
+        } else {
+            for (tdir = Wolf.Math.dir8_south; tdir >= Wolf.Math.dir8_east; tdir -= 2) { // * Revision (JDC fix for unsigned enums)
+                if (tdir != turnaround) {
+                    if (changeDir(self, tdir, level)) {
+                        return;
+                    }
+                }
+            }
+        }
+
+        if (turnaround != Wolf.Math.dir8_nodir) {
+            if (changeDir(self, turnaround, level)) {
+                return;
+            }
+        }
+
+        self.dir = Wolf.Math.dir8_nodir; // can't move
+    }
+
+
+    /**
+     * @description Run Away from player.  
+     * @private
+     */
+    function retreat(self, game) {
+        var level = game.level,
+            player = game.player,
+            deltax, 
+            deltay,
+            d = [],
+            tdir;
+
+        deltax = Wolf.POS2TILE(player.position.x) - Wolf.POS2TILE(self.x);
+        deltay = Wolf.POS2TILE(player.position.y) - Wolf.POS2TILE(self.y);
+
+        d[0] = deltax < 0 ? Wolf.Math.dir8_east  : Wolf.Math.dir8_west;
+        d[1] = deltay < 0 ? Wolf.Math.dir8_north : Wolf.Math.dir8_south;
+
+        if (Math.abs(deltay) > Math.abs(deltax)) {
+            tdir = d[0];
+            d[0] = d[1];
+            d[1] = tdir;
+        } // swap d[0] & d[1]
+
+        if (changeDir(self, d[0], level)) {
+            return;
+        }
+        if (changeDir(self, d[1], level)) {
+            return;
+        }
+
+        // there is no direct path to the player, so pick another direction
+        if (Wolf.Random.rnd() > 128) { // randomly determine direction of search
+            for(tdir = Wolf.Math.dir8_east; tdir <= Wolf.Math.dir8_south; tdir += 2 ) { // * Revision
+                if (changeDir(self, tdir, level)) {
+                    return;
+                }
+            }
+        } else {
+            for (tdir = Wolf.Math.dir8_south; tdir >= Wolf.Math.dir8_east; tdir -= 2) { // * Revision (JDC fix for unsigned enums)
+                if (changeDir(self, tdir, level)) {
+                    return;
+                }
+            }
+        }
+
+        self.dir = Wolf.Math.dir8_nodir;        // can't move
+    }
+
+    
+    /**
+     * @description Attempts to choose and initiate a movement for entity
+     *              that sends it towards the player while dodging.
+     * @private
+     */
+    function dodge(self, game) {
+        var level = game.level,
+            player = game.player,
+            deltax, 
+            deltay, 
+            i,
+            
+            dirtry = [], 
+            turnaround, 
+            tdir;
+            
+        if (game.player.playstate == Wolf.ex_victory) {
+            return;
+        }
+       
+        if (self.flags & Wolf.FL_FIRSTATTACK) {
+            // turning around is only ok the very first time after noticing the player
+            turnaround = Wolf.Math.dir8_nodir;
+            self.flags &= ~Wolf.FL_FIRSTATTACK;
+        } else {
+            turnaround = Wolf.Math.opposite8[self.dir];
+        }
+
+      
+        deltax = Wolf.POS2TILE(player.position.x) - Wolf.POS2TILE(self.x);
+        deltay = Wolf.POS2TILE(player.position.y) - Wolf.POS2TILE(self.y);
+
+        //
+        // arange 5 direction choices in order of preference
+        // the four cardinal directions plus the diagonal straight towards
+        // the player
+        //
+
+        if (deltax > 0) {
+            dirtry[1] = Wolf.Math.dir8_east;
+            dirtry[3] = Wolf.Math.dir8_west;
+        } else {
+            dirtry[1] = Wolf.Math.dir8_west;
+            dirtry[3] = Wolf.Math.dir8_east;
+        }
+
+        if( deltay > 0 ) {
+            dirtry[2] = Wolf.Math.dir8_north;
+            dirtry[4] = Wolf.Math.dir8_south;
+        } else {
+            dirtry[2] = Wolf.Math.dir8_south;
+            dirtry[4] = Wolf.Math.dir8_north;
+        }
+
+        // randomize a bit for dodging
+        if (Math.abs(deltax) > Math.abs(deltay)) {
+            tdir = dirtry[1]; dirtry[1]=dirtry[2]; dirtry[2]=tdir; // => swap dirtry[1] & dirtry[2]
+            tdir = dirtry[3]; dirtry[3]=dirtry[4]; dirtry[4]=tdir; // => swap dirtry[3] & dirtry[4]
+        }
+
+        if (Wolf.Random.rnd() < 128) {
+            tdir = dirtry[1]; dirtry[1]=dirtry[2]; dirtry[2]=tdir;
+            tdir = dirtry[3]; dirtry[3]=dirtry[4]; dirtry[4]=tdir;
+        }
+
+        dirtry[0] = Wolf.Math.diagonal[dirtry[1]][dirtry[2]];
+
+        // try the directions util one works
+        for (i=0; i < 5; ++i) {
+            if (dirtry[i] == Wolf.Math.dir8_nodir || dirtry[i] == turnaround) {
+                continue;
+            }
+            if (changeDir(self, dirtry[i], level)) {
+                return;
+            }
+        }
+
+        // turn around only as a last resort
+        if (turnaround != Wolf.Math.dir8_nodir) {
+            if (changeDir(self, turnaround, level)) {
+                return;
+            }
+        }
+        
+
+        
+        self.dir = Wolf.Math.dir8_nodir;
+    }
+    
+
+    /**
+     * @memberOf Wolf.AI
+     */
+    function T_Stand(self, game, tics) {
+        findTarget(self, game, tics);
+    }
+    
+    /**
+     * @memberOf Wolf.AI
+     */
+    function T_Path(self, game, tics) {
+        var level = game.level;
+        if (findTarget(self, game, tics)) {
+            return;
+        }
+
+        if (!self.speed) {
+            return; // if patroling with a speed of 0
+        }
+
+        if (self.dir == Wolf.Math.dir8_nodir) {
+            path(self, game);
+
+            if (self.dir == Wolf.Math.dir8_nodir) {
+                return; // all movement is blocked
+            }
+        }
+        T_Advance(self, game, path, tics);
+    }
+
+
+    /**
+     * @description Try to damage the player.
+     * @memberOf Wolf.AI
+     */
+    function T_Shoot(self, game, tics) {
+        var level = game.level,
+            player = game.player,
+            dx, dy, dist,
+            hitchance,
+            damage;
+
+        if (!level.state.areabyplayer[self.areanumber]) {
+            return;
+        }
+        
+        if (!Wolf.Level.checkLine(self.x, self.y, player.position.x, player.position.y, level)) {
+            return; // player is behind a wall
+        }
+
+        dx = Math.abs(Wolf.POS2TILE(self.x ) - Wolf.POS2TILE(player.position.x));
+        dy = Math.abs(Wolf.POS2TILE(self.y ) - Wolf.POS2TILE(player.position.y));
+        dist = Math.max(dx, dy);
+
+        if (self.type == Wolf.en_ss || self.type == Wolf.en_boss )
+        {
+            dist = dist * 2 / 3;                    // ss are better shots
+        }
+
+        if (player.speed >= Wolf.RUNSPEED) {
+            hitchance = 160;
+        } else {
+            hitchance = 256;
+        }
+
+        // if guard is visible by player
+        // player can see to dodge
+        // (if CheckLine both player & enemy see each other)
+        // So left only check if guard is in player's fov: FIXME: not fixed fov!
+        var trans = Wolf.Math.transformPoint(self.x, self.y, player.position.x, player.position.y);
+        if (Wolf.Angle.diff(trans, Wolf.FINE2DEG(player.angle)) < (Math.PI/3)) {
+            hitchance -= dist * 16;
+        } else {
+            hitchance -= dist * 8;
+        }
+
+        // see if the shot was a hit
+        if (Wolf.Random.rnd() < hitchance) {
+            if (dist < 2) {
+                damage = Wolf.Random.rnd() >> 2;
+            } else if (dist < 4) {
+                damage = Wolf.Random.rnd() >> 3;
+            } else {
+                damage = Wolf.Random.rnd() >> 4;
+            }
+            Wolf.Player.damage(player, self, damage);
+        }
+
+        switch (self.type) {
+            case Wolf.en_ss:
+                Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "sfx/024.wav", 1, Wolf.ATTN_NORM, 0);
+                break;
+            case Wolf.en_gift:
+            case Wolf.en_fat:
+            case Wolf.en_mecha:
+            case Wolf.en_hitler:
+            case Wolf.en_boss:
+                Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "sfx/022.wav", 1, Wolf.ATTN_NORM, 0);
+                break;
+            default:
+                Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "sfx/049.wav", 1, Wolf.ATTN_NORM, 0);
+                break;
+        }
+    }
+    
+
+    /**
+     * @description 
+     * @memberOf Wolf.AI
+     */
+    function T_Chase(self, game, tics) {
+        var level = game.level,
+            player = game.player,
+            dx, dy, 
+            dist, 
+            chance,
+            shouldDodge = false;
+        
+        // if (gamestate.victoryflag) return;
+        if (Wolf.Level.checkLine(self.x, self.y, player.position.x, player.position.y, level)) { // got a shot at player?
+            dx = Math.abs(Wolf.POS2TILE(self.x) - Wolf.POS2TILE(player.position.x));
+            dy = Math.abs(Wolf.POS2TILE(self.y) - Wolf.POS2TILE(player.position.y));
+            dist = Math.max(dx, dy);
+            if (!dist || (dist == 1 && self.distance < 16)) {
+                chance = 300;
+            } else {
+                chance = (tics << 4) / dist; // 100/dist;
+            }
+
+            if (Wolf.Random.rnd() < chance) {
+                // go into attack frame
+                Wolf.Actors.stateChange(self, Wolf.st_shoot1);
+                return;
+            }
+            shouldDodge = true;
+        }
+        
+
+        if (self.dir == Wolf.Math.dir8_nodir) {
+            if (shouldDodge) {
+                dodge(self, game);
+            } else {
+                chase(self, game);
+            }
+
+            if (self.dir == Wolf.Math.dir8_nodir) {
+                return; // object is blocked in
+            }
+            self.angle = Wolf.Math.dir8angle[self.dir];
+        }
+
+        T_Advance(self, game, shouldDodge ? dodge : chase, tics);
+
+    }
+    
+
+    /**
+     * @description 
+     * @memberOf Wolf.AI
+     */
+    function T_DogChase(self, game, tics) {
+        var level = game.level,
+            player = game.player,
+            dx, dy;
+
+        if (self.dir == Wolf.Math.dir8_nodir) {
+            dodge(self, game);
+            self.angle = Wolf.Math.dir8angle[ self.dir ];
+            if (self.dir == Wolf.Math.dir8_nodir) {
+                return; // object is blocked in
+            }
+        }
+
+        //
+        // check for bite range
+        //
+        dx = Math.abs(player.position.x - self.x) - Wolf.TILEGLOBAL / 2;
+        if (dx <= Wolf.MINACTORDIST) {
+            dy = Math.abs(player.position.y - self.y) - Wolf.TILEGLOBAL / 2;
+            if (dy <= Wolf.MINACTORDIST) {
+                Wolf.Actors.stateChange(self, Wolf.st_shoot1);
+                return; // bite player!
+            }
+        }
+
+        T_Advance(self, game, dodge, tics);
+    }
+    
+
+    /**
+     * @description Try to damage the player.
+     * @memberOf Wolf.AI
+     */
+    function T_BossChase(self, game, tics) {
+        var level = game.level,
+            player = game.player,
+            dx, dy, dist,
+            think,
+            shouldDodge = false;
+
+        dx = Math.abs(self.tile.x - Wolf.POS2TILE(player.position.x));
+        dy = Math.abs(self.tile.y - Wolf.POS2TILE(player.position.y));
+        dist = Math.max(dx, dy);
+
+        if (Wolf.Level.checkLine(self.x, self.y, player.position.x, player.position.y, level)) {
+            // got a shot at player?
+            if (Wolf.Random.rnd() < tics << 3) {
+                // go into attack frame
+                Wolf.Actors.stateChange(self, Wolf.st_shoot1);
+                return;
+            }
+            shouldDodge = true;
+        }
+
+        if( self.dir == Wolf.Math.dir8_nodir ) {
+            if (shouldDodge) {
+                dodge(self, game);
+            } else {
+                chase(self, game);
+            }
+
+            if( self.dir == Wolf.Math.dir8_nodir ) {
+                // object is blocked in
+                return;    
+            }
+        }
+
+        think = dist < 4 ? retreat : (shouldDodge ? dodge : chase);
+        T_Advance(self, game, think, tics);
+    }
+    
+    
+    /**
+     * @description 
+     * @memberOf Wolf.AI
+     */
+    function T_Fake(self, game, tics) {
+        var level = game.level,
+            player = game.player;
+            
+        if (Wolf.Level.checkLine(self.x, self.y, player.position.x, player.position.y, level)) {
+            if (Wolf.Random.rnd() < tics << 1) {
+                // go into attack frame
+                Wolf.Actors.stateChange(self, Wolf.st_shoot1);
+                return;
+            }
+        }
+        
+        if (self.dir == Wolf.Math.dir8_nodir) {
+            dodge(self, game);
+            if (self.dir == Wolf.Math.dir8_nodir ) {
+                // object is blocked in
+                return;
+            }
+        }
+
+        T_Advance(self, game, dodge, tics);
+    }
+
+    
+    /**
+     * @description 
+     * @private
+     */
+    function T_Advance(self, game, think, tics) {
+        var level = game.level,
+            move, door;
+
+        if (!think) {
+            Wolf.log("Warning: Advance without <think> proc\n");
+            return;
+        }
+
+        move = self.speed * tics;
+        while (move > 0) {
+            // waiting for a door to open
+            if (self.waitfordoorx) {
+                door = level.state.doorMap[self.waitfordoorx][self.waitfordoory];
+
+                Wolf.Doors.open(door);
+                if (door.action != Wolf.dr_open) {
+                    return; // not opened yet...
+                }
+                self.waitfordoorx = self.waitfordoory = 0;    // go ahead, the door is now open
+            }
+
+            if (move < self.distance ) {
+                T_Move(self, game, move);
+                break;
+            }
+
+            // fix position to account for round off during moving
+            self.x = Wolf.TILE2POS(self.tile.x);
+            self.y = Wolf.TILE2POS(self.tile.y);
+
+            move -= self.distance;
+
+            // think: Where to go now?
+            think(self, game, tics);
+            
+            self.angle = Wolf.Math.dir8angle[self.dir];
+            if (self.dir == Wolf.Math.dir8_nodir) {
+                return; // all movement is blocked
+            }
+        }
+    }
+    
+    /**
+     * @description Moves object for distance in global units, in self.dir direction. 
+     * @memberOf Wolf.AI
+     */
+    function T_Move(self, game, dist) {
+        var level = game.level,
+            player = game.player;
+            
+        if (self.dir == Wolf.Math.dir8_nodir || !dist) {
+            return;
+        }
+        self.x += dist * Wolf.Math.dx8dir[self.dir];
+        self.y += dist * Wolf.Math.dy8dir[self.dir];
+
+        // check to make sure it's not on top of player
+        if (Math.abs(self.x - player.position.x) <= Wolf.MINACTORDIST) {
+            if (Math.abs(self.y - player.position.y) <= Wolf.MINACTORDIST) {
+                var t = self.type;
+                if (t == Wolf.en_blinky  || t == Wolf.en_clyde || t == Wolf.en_pinky || t == Wolf.en_inky || t == Wolf.en_spectre) {
+                    Wolf.Player.damage(player, self, 2); // ghosts hurt player!
+                }
+                //
+                // back up
+                //
+                self.x -= dist * Wolf.Math.dx8dir[self.dir];
+                self.y -= dist * Wolf.Math.dy8dir[self.dir];
+                return;
+            }
+        }
+
+        self.distance -= dist;
+        if (self.distance < 0) {
+            self.distance = 0;
+        }
+    }
+    
+    /**
+     * @description 
+     * @memberOf Wolf.AI
+     */
+    function T_Ghosts(self, game, tics) {
+        var level = game.level,
+            player = game.player;
+
+        if (self.dir == Wolf.Math.dir8_nodir) {
+            chase(self, game);
+            if (self.dir == Wolf.Math.dir8_nodir ) {
+                return;    // object is blocked in
+            }
+            self.angle = Wolf.Math.dir8angle[self.dir];
+        }
+        T_Advance(self, game, chase, tics);
+    }
+    
+    /**
+     * @description 
+     * @memberOf Wolf.AI
+     */
+    function T_Bite(self, game, tics) {
+        var level = game.level,
+            player = game.player,
+            dx, dy;
+
+        Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_VOICE, "sfx/002.wav", 1, Wolf.ATTN_NORM, 0);
+
+        dx = Math.abs(player.position.x - self.x) - Wolf.TILEGLOBAL;
+        if (dx <= Wolf.MINACTORDIST) {
+            dy = Math.abs(player.position.y - self.y) - Wolf.TILEGLOBAL;
+            if (dy <= Wolf.MINACTORDIST) {
+                if (Wolf.Random.rnd() < 180) {
+                    Wolf.Player.damage(player, self, Wolf.Random.rnd() >> 4);
+                    return;
+                }
+            }
+        }
+    }
+
+
+    /**
+     * @description 
+     * @memberOf Wolf.AI
+     */
+    function T_UShoot(self, game, tics) {
+        var level = game.level,
+            player = game.player,
+            dx, dy,
+            dist;
+
+        T_Shoot(self, game, tics);
+
+        dx = Math.abs(self.tile.x - Wolf.POS2TILE(player.position.x));
+        dy = Math.abs(self.tile.y - Wolf.POS2TILE(player.position.y));
+        dist = Math.max(dx, dy);
+
+        if (dist <= 1) {
+            Wolf.Player.damage(player, self, 10);
+        }
+    }
+    
+    
+    /**
+     * @description 
+     * @memberOf Wolf.AI
+     */
+    function T_Launch(self, game, tics) {
+        var level = game.level,
+            player = game.player,
+            proj, iangle;
+
+        iangle = Wolf.Math.transformPoint(self.x, self.y, player.position.x, player.position.y) + Math.PI;
+        if (iangle > 2 * Math.PI) {
+            iangle -= 2 * Math.PI;
+        }
+
+        if (self.type == Wolf.en_death) {
+            // death knight launches 2 rockets with 4 degree shift each.
+            T_Shoot(self, game, tics);
+            if (self.state == Wolf.st_shoot2) {
+                iangle = Wolf.Math.normalizeAngle(iangle - Wolf.DEG2RAD(4));
+            } else {
+                iangle = Wolf.Math.normalizeAngle(iangle + Wolf.DEG2RAD(4));
+            }
+        }
+
+        proj = Wolf.Actors.getNewActor(level);
+        if (proj == null) {
+            return;
+        }
+
+        proj.x = self.x;
+        proj.y = self.y;
+
+        proj.tile.x = self.tile.x;
+        proj.tile.y = self.tile.y;
+
+        proj.state = Wolf.st_stand;
+        proj.ticcount = 1;
+        proj.dir = Wolf.Math.dir8_nodir;
+
+        proj.angle = Wolf.RAD2FINE(iangle)>>0;
+
+        proj.speed = 0x2000;
+        proj.flags = Wolf.FL_NONMARK; // FL_NEVERMARK;
+        proj.sprite = Wolf.Sprites.getNewSprite(level);
+        
+        switch(self.type) {
+            case Wolf.en_death:
+                proj.type = Wolf.en_hrocket;
+                Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "lsfx/078.wav", 1, Wolf.ATTN_NORM, 0);
+                break;
+
+            case Wolf.en_angel:
+                proj.type = Wolf.en_spark;
+                proj.state = Wolf.st_path1;
+                Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "lsfx/069.wav", 1, Wolf.ATTN_NORM, 0);
+                break;
+
+            case Wolf.en_fake:
+                proj.type = Wolf.en_fire;
+                proj.state = Wolf.st_path1;
+                proj.flags = Wolf.FL_NEVERMARK;
+                proj.speed = 0x1200;
+                Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "lsfx/069.wav", 1, Wolf.ATTN_NORM, 0);
+                break;
+
+            case Wolf.en_schabbs:
+                proj.type = Wolf.en_needle;
+                proj.state = Wolf.st_path1;
+                Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "lsfx/008.wav", 1, Wolf.ATTN_NORM, 0);
+                break;
+
+            default:
+                proj.type = Wolf.en_rocket;
+                Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "lsfx/085.wav", 1, Wolf.ATTN_NORM, 0);
+        }
+        
+    }
+    
+
+    
+    /**
+     * @description Called when projectile is airborne. 
+     * @private
+     * @param {object} self The projectile actor object.
+     * @param {object} level The level object.
+     * @returns {boolean} True if move ok, otherwise false.
+     */
+    function projectileTryMove(self, level) {
+        var PROJSIZE = 0x2000,
+            xl, yl, xh, yh, x, y;
+
+        xl = (self.x - PROJSIZE) >> Wolf.TILESHIFT;
+        yl = (self.y - PROJSIZE) >> Wolf.TILESHIFT;
+
+        xh = (self.x + PROJSIZE) >> Wolf.TILESHIFT;
+        yh = (self.y + PROJSIZE) >> Wolf.TILESHIFT;
+
+        // Checking for solid walls:
+        for (y = yl; y <= yh; ++y) {
+            for (x = xl; x <= xh; ++x) {
+                // FIXME: decide what to do with statics & Doors!
+                if (level.tileMap[x][y] & (Wolf.WALL_TILE | Wolf.BLOCK_TILE)) {
+                    return false;
+                }
+                if (level.tileMap[x][y] & Wolf.DOOR_TILE) {
+                    if (Wolf.Doors.opened(level.state.doorMap[x][y]) != Wolf.DOOR_FULLOPEN) {
+                        return false;
+                    }
+                }
+            }
+        }
+        // FIXME: Projectile will fly through objects (even guards & columns) - must fix to create rocket launcher!
+        return true;
+    }
+
+
+    /**
+     * @description Called when projectile is airborne. 
+     * @memberOf Wolf.AI
+     * @param {object} self The enemy actor object.
+     * @param {object} level The level object.
+     * @param {object} player The player object.
+     * @param {number} tics The number of tics.
+     * @returns {boolean} True if move ok, otherwise false.
+     */
+    function T_Projectile(self, game, tics) {
+        var level = game.level,
+            player = game.player,
+            PROJECTILESIZE = 0xC000,
+            deltax, deltay, 
+            speed, damage;
+        
+        speed = self.speed * tics;
+
+        deltax = (speed * Wolf.Math.CosTable[self.angle])>>0;
+        deltay = (speed * Wolf.Math.SinTable[self.angle])>>0;
+
+        if (deltax > Wolf.TILEGLOBAL) {
+            deltax = Wolf.TILEGLOBAL;
+        }
+        if (deltax < -Wolf.TILEGLOBAL) {
+            deltax = -Wolf.TILEGLOBAL; // my
+        }
+        if (deltay > Wolf.TILEGLOBAL) {
+            deltay = Wolf.TILEGLOBAL;
+        }
+        if (deltay < -Wolf.TILEGLOBAL) {
+            deltay = -Wolf.TILEGLOBAL; // my
+        }
+
+        self.x += deltax;
+        self.y += deltay;
+
+        deltax = Math.abs(self.x - player.position.x);
+        deltay = Math.abs(self.y - player.position.y);
+
+        if (!projectileTryMove(self, level)) {
+            if (self.type == Wolf.en_rocket || self.type == Wolf.en_hrocket ) { 
+                // rocket ran into obstacle, draw explosion!
+                Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "lsfx/086.wav", 1, Wolf.ATTN_NORM, 0);
+                Wolf.Actors.stateChange(self, Wolf.st_die1);
+            } else {
+                Wolf.Actors.stateChange(self, Wolf.st_remove); // mark for removal
+            }
+            return;
+        }
+
+        if (deltax < PROJECTILESIZE && deltay < PROJECTILESIZE) {
+            // hit the player
+            switch (self.type) {
+                case Wolf.en_needle:
+                    damage = (Wolf.Random.rnd() >> 3) + 20;
+                    break;
+
+                case Wolf.en_rocket:
+                case Wolf.en_hrocket:
+                case Wolf.en_spark:
+                    damage = (Wolf.Random.rnd()>>3) + 30;
+                    break;
+
+                case Wolf.en_fire:
+                    damage = (Wolf.Random.rnd() >> 3);
+                    break;
+
+                default:
+                    damage = 0;
+                    break;
+            }
+
+            Wolf.Player.damage(player, self, damage);
+            Wolf.Actors.stateChange(self, Wolf.st_remove); // mark for removal
+            return;
+        }
+
+        self.tile.x = self.x >> Wolf.TILESHIFT;
+        self.tile.y = self.y >> Wolf.TILESHIFT;
+    }
+    
+    /**
+     * @description 
+     * @memberOf Wolf.AI
+     */
+    function T_BJRun(self, game, tics) {
+        var move = Wolf.BJRUNSPEED * tics;
+
+        T_Move(self, game, move);
+
+        if (!self.distance) {
+            self.distance = Wolf.TILEGLOBAL;
+            if (!(--self.temp2)) {
+                Wolf.Actors.stateChange(self, Wolf.st_shoot1);
+                self.speed = Wolf.BJJUMPSPEED;
+                return;
+            }
+        }
+    }
+    
+    /**
+     * @description 
+     * @memberOf Wolf.AI
+     */
+    function T_BJJump(self, game, tics) {
+        //var move = Wolf.BJRUNSPEED * tics;
+        //T_Move(self, game, move);
+    }
+    
+    /**
+     * @description 
+     * @memberOf Wolf.AI
+     */
+    function T_BJYell(self, game, tics) {
+        Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_VOICE, "sfx/082.wav", 1, Wolf.ATTN_NORM, 0);
+    }
+    
+    /**
+     * @description 
+     * @memberOf Wolf.AI
+     */
+    function T_BJDone(self, game, tics) {
+        Wolf.Player.playstate = Wolf.ex_victory; // exit castle tile
+        //Wolf.Player.playstate = Wolf.ex_complete;
+        Wolf.Game.endEpisode(game);
+    }
+
+    
+    return {
+        T_Stand : T_Stand,
+        T_Path : T_Path,
+        T_Ghosts : T_Ghosts,
+        T_Bite : T_Bite,
+        T_Shoot : T_Shoot,
+        T_UShoot : T_UShoot,
+        T_Launch : T_Launch,
+        T_Chase : T_Chase,
+        T_DogChase : T_DogChase,
+        T_BossChase : T_BossChase,
+        T_Fake : T_Fake,
+        T_Projectile : T_Projectile,
+        T_BJRun : T_BJRun,
+        T_BJJump : T_BJJump,
+        T_BJYell : T_BJYell,
+        T_BJDone : T_BJDone
+    };
+    
+})();
\ No newline at end of file
diff --git a/js/angle.js b/js/angle.js
new file mode 100644 (file)
index 0000000..293f61c
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+* ===========================================================================
+* 
+* Wolf3D Browser Version GPL Source Code
+* Copyright (C) 2012 id Software LLC, a ZeniMax Media company. 
+* 
+* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").  
+* 
+* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+* 
+* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+* 
+* You should have received a copy of the GNU General Public License version 2
+* along with Wolf3D Browser Source Code.  If not, see <http://www.gnu.org/licenses/>.
+* 
+* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
+* 
+* ===========================================================================
+*/
+
+/** 
+ * @namespace 
+ * @description Angle math
+ */
+Wolf.Angle = (function() {
+
+    Wolf.setConsts({
+        DEG2RAD         : function(a) { return a * 0.01745329251994329576; }, // a * M_PI / 180.0f
+        RAD2DEG         : function(a) { return a / 0.01745329251994329576; }, // a * 180.0f / M_PI 
+        ANGLE2SHORT     : function(x) {        return ((x * 65536 / 360)>>0) & 65535; },
+        SHORT2ANGLE     : function(x) {        return x * 360.0 / 65536; }
+    });
+
+    /**
+     * @description Finds the difference between two angles.
+     * @memberOf Wolf.Angle
+     * @param {number} angle1 Angle in radians.
+     * @param {number} angle2 Angle in radians.
+     * @returns {number} The absolute difference between two angles, this will always be between 0 and 180 degrees.
+     */
+    function diff(angle1, angle2) {
+        var d;
+
+        if (angle1 > angle2) {
+            d = angle1 - angle2;
+        } else {
+            d = angle2 - angle1;
+        }
+
+        if (d > Math.PI) {
+            return 2 * Math.PI - d;
+        } else {
+            return d;
+        }
+    }
+
+    /**
+     * @description Clockwise distance between two angles.
+     * @memberOf Wolf.Angle
+     * @param {number} angle1 Angle in radians.
+     * @param {number} angle2 Angle in radians.
+     * @returns {number} The clockwise distance from angle2 to angle1, this may be greater than 180 degrees.
+     */
+    function distCW(angle1, angle2) {
+        if (angle1 > angle2) {
+            return angle1 - angle2;
+        } else {
+            return angle1 + 2 * Math.PI - angle2;
+        }
+    }
+
+    /**
+     * @description Linear interpolate between angle from and to by fraction frac.
+     * @memberOf Wolf.Angle
+     * @param {number} from Angle in radians.
+     * @param {number} to Angle in radians.
+     * @param {number} frac Fraction.
+     * @returns {number}
+     */
+    function interpolate(from, to, frac) {
+        var d = diff(from, to) * frac;
+
+        if (distCW(to, from) >= Math.PI) {
+            return from - diff;
+        } else {
+            return from + diff;
+        }
+    }
+
+
+    /**
+     * @description Normalize angle.
+     * @memberOf Wolf.Angle
+     * @param {number} angle
+     * @returns {number}
+     */
+    function normalize(angle) {
+        while (angle < 0) {
+            angle += (2 * Math.PI);
+        }
+        while (angle >= (2 * Math.PI)) {
+            angle -= (2 * Math.PI);
+        }
+        return angle;
+    }
+
+
+
+    /**
+     * @description Linear interpolate allowing for the Modulo 360 problem.
+     * @memberOf Wolf.Angle
+     * @param {number} from Angle in radians.
+     * @param {number} to Angle in radians.
+     * @param {number} frac fraction.
+     * @returns {number}
+     */
+
+    function lerp(from, to, frac) {
+        if (to - from > 180) {
+            to -= 360;
+        }
+        if (to - from < -180) {
+            to += 360;
+        }
+        return from + frac * (to - from);
+    }
+
+    return {
+        diff : diff,
+        distCW : distCW,
+        normalize : normalize,
+        interpolate : interpolate,
+        lerp : lerp
+    }
+
+})();
\ No newline at end of file
diff --git a/js/areas.js b/js/areas.js
new file mode 100644 (file)
index 0000000..5cf0a4b
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+* ===========================================================================
+* 
+* Wolf3D Browser Version GPL Source Code
+* Copyright (C) 2012 id Software LLC, a ZeniMax Media company. 
+* 
+* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").  
+* 
+* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+* 
+* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+* 
+* You should have received a copy of the GNU General Public License version 2
+* along with Wolf3D Browser Source Code.  If not, see <http://www.gnu.org/licenses/>.
+* 
+* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
+* 
+* ===========================================================================
+*/
+
+/** 
+ * @namespace 
+ * @description Area management
+ */
+Wolf.Areas = (function() {
+    /*
+        Notes:
+
+        Open doors connect two areas, so sounds will travel between them and sight
+        will be checked when the player is in a connected area.
+
+        Areaconnect is incremented/decremented by each door. If >0 they connect.
+
+        Every time a door opens or closes the areabyplayer matrix gets recalculated.
+        An area is true if it connects with the player's current spor.
+
+    */
+    
+    /**
+     * @description Initialize areas 
+     * @memberOf Wolf.Areas
+     * @param {object} levelState The level state object
+     * @param {number} areanumber Initial area
+     */
+    function init(level, areanumber) {
+        level.state.areaconnect = [];
+        level.state.areabyplayer = [];
+        for (var i=0;i<Wolf.NUMAREAS;i++) {
+            level.state.areaconnect[i] = [];
+            for (var j=0;j<Wolf.NUMAREAS;j++) {
+                level.state.areaconnect[i][j] = 0;
+            }
+            level.state.areabyplayer[i] = false;
+        }
+        level.state.areabyplayer[areanumber] = true;
+    }
+    
+    
+    /**
+     * @private
+     * @description Scans outward from playerarea, marking all connected areas. 
+     * @param {object} level The level object
+     * @param {number} areanumber Area
+     */
+    function recursiveConnect(level, areanumber) {
+        for (var i = 0;i < Wolf.NUMAREAS; ++i) {
+            if (level.state.areaconnect[areanumber][i] && !level.state.areabyplayer[i]) {
+                level.state.areabyplayer[i] = true;
+                recursiveConnect(level, i);
+            }
+        }
+    }
+
+    /**
+     * @description Connect area. 
+     * @memberOf Wolf.Areas
+     * @param {object} level The level object
+     * @param {number} areanumber New area
+     */
+    function connect(level, areanumber) {
+        var    i, c = 0;
+        
+        if (areanumber >= Wolf.NUMAREAS) {
+            throw new Error("areanumber >= Wolf.NUMAREAS");
+        }
+        
+        level.state.areabyplayer = [];
+        level.state.areabyplayer[areanumber] = true;
+        
+        recursiveConnect(level, areanumber);
+        for (i = 0; i < Wolf.NUMAREAS; i++) {
+            if (level.state.areabyplayer[i]) {
+                c++;
+            }
+        }
+    }
+
+    /**
+     * @description Join ares 
+     * @memberOf Wolf.Areas
+     * @param {object} level The level object
+     * @param {number} area1 Area 1
+     * @param {number} area2 Area 2
+     */
+    function join(level, area1, area2) {
+        if (area1 < 0 || area1 >= Wolf.NUMAREAS) {
+            throw new Error("area1 < 0 || area1 >= Wolf.NUMAREAS");
+        }
+        if (area2 < 0 || area2 >= Wolf.NUMAREAS) {
+            throw new Error("area2 < 0 || area2 >= Wolf.NUMAREAS");
+        }
+        level.state.areaconnect[area1][area2]++;
+        level.state.areaconnect[area2][area1]++;
+    }
+
+    /**
+     * @description Disconnect ares 
+     * @memberOf Wolf.Areas
+     * @param {object} level The level object
+     * @param {number} area1 Area 1
+     * @param {number} area2 Area 2
+     */
+    function disconnect(level, area1, area2) {
+        if (area1 < 0 || area1 >= Wolf.NUMAREAS) {
+            throw new Error("area1 < 0 || area1 >= Wolf.NUMAREAS");
+        }
+        if (area2 < 0 || area2 >= Wolf.NUMAREAS) {
+            throw new Error("area2 < 0 || area2 >= Wolf.NUMAREAS");
+        }
+        level.state.areaconnect[area1][area2]--;
+        level.state.areaconnect[area2][area1]--;
+    }
+
+    return {
+        init : init,
+        connect : connect,
+        join : join,
+        disconnect : disconnect
+    };
+
+})();
+
diff --git a/js/doors.js b/js/doors.js
new file mode 100644 (file)
index 0000000..7a16264
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+* ===========================================================================
+* 
+* Wolf3D Browser Version GPL Source Code
+* Copyright (C) 2012 id Software LLC, a ZeniMax Media company. 
+* 
+* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").  
+* 
+* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+* 
+* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+* 
+* You should have received a copy of the GNU General Public License version 2
+* along with Wolf3D Browser Source Code.  If not, see <http://www.gnu.org/licenses/>.
+* 
+* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
+* 
+* ===========================================================================
+*/
+
+/** 
+ * @namespace 
+ * @description Door management
+ */
+Wolf.Doors = (function() {
+
+    Wolf.setConsts({
+        CLOSEWALL       : Wolf.MINDIST, // Space between wall & player
+        MAXDOORS        : 64,           // max number of sliding doors
+
+        MAX_DOORS       : 256, // jseidelin: doesn't look like this is used?
+        DOOR_TIMEOUT    : 300,
+        DOOR_MINOPEN    : 50,
+        DOOR_FULLOPEN   : 63,
+        DOOR_VERT       : 255,
+        DOOR_HORIZ      : 254,
+        DOOR_E_VERT     : 253,
+        DOOR_E_HORIZ    : 252,
+        DOOR_G_VERT     : 251,
+        DOOR_G_HORIZ    : 250,
+        DOOR_S_VERT     : 249,
+        DOOR_S_HORIZ    : 248,
+        FIRST_DOOR      : 248,
+        LAST_LOCK       : 251,
+        
+        TEX_DOOR        : 98,
+        // TEX_DOOR        : 126,
+        
+        dr_closing      : -1,
+        dr_closed       : 0,
+        dr_opening      : 1,
+        dr_open         : 2
+    });
+    Wolf.setConsts({
+        // texture IDs used by cache routines
+        TEX_DDOOR       : (0 + Wolf.TEX_DOOR), // Simple Door
+        TEX_PLATE       : (2 + Wolf.TEX_DOOR), // Door Plate
+        TEX_DELEV       : (4 + Wolf.TEX_DOOR), // Elevator Door
+        TEX_DLOCK        : (6 + Wolf.TEX_DOOR)  // Locked Door
+    });
+
+
+    /**
+     * @description Reset doors in the level
+     * @memberOf Wolf.Doors
+     * @param {object} level The level object.
+     */
+    function reset(level) {
+        level.state.numDoors = 0;
+
+        for (var x=0;x<64;x++) {
+            level.state.doorMap[x] = [];
+            for (var y=0;y<64;y++) {
+                level.state.doorMap[x][y] = 0;
+            }
+        }
+    }
+
+    /**
+     * @description Spawn a door at the specified position.
+     * @memberOf Wolf.Doors
+     * @param {object} level The level object.
+     * @param {number} x The x coordinate.
+     * @param {number} y The y coordinate.
+     * @param {number} type The door type.
+     * @returns {number} The index of the new door.
+     */
+    function spawn(level, x, y, type) {
+        if (level.state.numDoors >= Wolf.MAXDOORS) {
+            throw new Error("Too many Doors on level!");
+        }
+        var door = level.state.doorMap[x][y] = {
+                type : -1,
+                vertical : 0,
+                texture : -1,
+                ticcount : 0
+            };
+
+        switch(type) {
+            case 0x5A:
+                door.type       = Wolf.DOOR_VERT;
+                door.vertical   = true;
+                door.texture    = Wolf.TEX_DDOOR + 1;
+                break;
+            case 0x5B:
+                door.type       = Wolf.DOOR_HORIZ;
+                door.vertical   = false;
+                door.texture    = Wolf.TEX_DDOOR;
+                break;
+            case 0x5C:
+                door.type       = Wolf.DOOR_G_VERT;
+                door.vertical   = true;
+                door.texture    = Wolf.TEX_DLOCK;
+                break;
+            case 0x5D:
+                door.type       = Wolf.DOOR_G_HORIZ;
+                door.vertical   = false;
+                door.texture    = Wolf.TEX_DLOCK;
+                break;
+            case 0x5E:
+                door.type       = Wolf.DOOR_S_VERT;
+                door.vertical   = true;
+                door.texture    = Wolf.TEX_DLOCK + 1;
+                break;
+            case 0x5F:
+                door.type       = Wolf.DOOR_S_HORIZ;
+                door.vertical   = false;
+                door.texture    = Wolf.TEX_DLOCK + 1;
+                break;
+            case 0x64:
+                door.type       = Wolf.DOOR_E_VERT;
+                door.vertical   = true;
+                door.texture    = Wolf.TEX_DELEV + 1;
+                break;
+            case 0x65:
+                door.type       = Wolf.DOOR_E_HORIZ;
+                door.vertical   = false;
+                door.texture    = Wolf.TEX_DELEV;
+                break;
+            default:
+                throw new Error("Unknown door type: " + type);
+        }
+
+        door.tile = {
+            x : x,
+            y : y
+        };
+        door.action = Wolf.dr_closed;
+
+        level.state.doors[level.state.numDoors] = door;
+        level.state.numDoors++;
+
+        return level.state.numDoors - 1;
+    }
+
+    /**
+     * @description Check to see if a door is open. If there are no doors in tile assume a closed door!
+     * @memberOf Wolf.Doors
+     * @param {object} doors The door object.
+     * @returns {number} DOOR_FULLOPEN if door is opened, 
+                         0 if door is closed, 
+                         >0 <DOOR_FULLOPEN if partially opened.
+     */
+    function opened(door) {     
+        return door.action == Wolf.dr_open ? Wolf.DOOR_FULLOPEN : door.ticcount;
+    }
+
+    
+    /**
+     * @description Process door actions.
+     * @memberOf Wolf.Doors
+     * @param {object} level The level object
+     * @param {object} player The player object
+     * @param {number} tics Tics since last
+     */
+    function process(level, player, tics) {
+        if (player.playstate == Wolf.ex_victory) {
+            return;
+        }
+
+        for (var n=0;n<level.state.numDoors;++n) {
+            var door = level.state.doors[n],
+                doorPos = {
+                    x : Wolf.TILE2POS(door.tile.x),
+                    y : Wolf.TILE2POS(door.tile.y)
+                };
+            switch (door.action) {
+                case Wolf.dr_closed: // this door is closed!
+                    continue;
+                    
+                case Wolf.dr_opening:
+                    if (door.ticcount >= Wolf.DOOR_FULLOPEN) { // door fully opened!
+                        door.action = Wolf.dr_open;
+                        door.ticcount = 0;
+                    } else { // opening!
+                        if (door.ticcount == 0) {
+                            // door is just starting to open, so connect the areas
+                            Wolf.Areas.join(level, door.area1, door.area2);
+                            Wolf.Areas.connect(level, player.areanumber);
+                            
+                            if (level.state.areabyplayer[door.area1]) { // Door Opening sound!
+                                Wolf.Sound.startSound(player.position, doorPos, 1, Wolf.CHAN_AUTO, "sfx/010.wav", 1, Wolf.ATTN_STATIC, 0);
+                            }
+                        }
+
+                        door.ticcount += tics;
+
+                        if (door.ticcount > Wolf.DOOR_FULLOPEN) {
+                            door.ticcount = Wolf.DOOR_FULLOPEN;
+                        }
+                    }
+                    break;
+
+                case Wolf.dr_closing:
+                    if (door.ticcount <= 0) { // door fully closed! disconnect areas!
+                        Wolf.Areas.disconnect(level, door.area1, door.area2);
+                        Wolf.Areas.connect(level, player.areanumber);
+                        door.ticcount = 0;
+                        door.action = Wolf.dr_closed;
+                    } else { // closing!
+                        if (door.ticcount == Wolf.DOOR_FULLOPEN) {
+                            if (level.state.areabyplayer[door.area1]) { // Door Closing sound!
+                                Wolf.Sound.startSound(player.position, doorPos, 1, Wolf.CHAN_AUTO, "sfx/007.wav", 1, Wolf.ATTN_STATIC, 0);
+                            }
+                        }
+                        door.ticcount -= tics;
+                        if (door.ticcount < 0) {
+                            door.ticcount = 0;
+                        }
+                    }
+                    break;
+
+                case Wolf.dr_open:
+                    if (door.ticcount > Wolf.DOOR_MINOPEN) {
+                        // If player or something is in door do not close it!
+                        if (!canCloseDoor(level, player, door.tile.x, door.tile.y, door.vertical)) {
+                            door.ticcount = Wolf.DOOR_MINOPEN; // do not close door immediately!
+                        }
+                    }
+                    if (door.ticcount >= Wolf.DOOR_TIMEOUT) {
+                        // Door timeout, time to close it!
+                        door.action = Wolf.dr_closing;
+                        door.ticcount = Wolf.DOOR_FULLOPEN;
+                    } else {
+                        // Increase timeout!
+                        door.ticcount += tics;
+                    }
+                    break;
+            } // End switch lvldoors->Doors[ n ].action        
+        } // End for n = 0 ; n < lvldoors->numDoors ; ++n 
+    }
+
+    /**
+     * @description Set the areas doors in a level
+     * @memberOf Wolf.Doors
+     * @param {object} level The level object.
+     * @param {array} areas The areas map.
+     */
+    function setAreas(level) {
+        var n, x, y,
+            door;
+        for (n=0; n<level.state.numDoors ; ++n){
+            door = level.state.doors[n];
+            x = door.tile.x;
+            y = door.tile.y;
+
+            if (door.vertical) {
+                door.area1 = level.areas[x + 1][y] >= 0 ? level.areas[x + 1][y] : 0;
+                door.area2 = level.areas[x - 1][y] >= 0 ? level.areas[x - 1][y] : 0;
+            } else {
+                door.area1 = level.areas[x][y + 1] >= 0 ? level.areas[x][y + 1] : 0;
+                door.area2 = level.areas[x][y - 1] >= 0 ? level.areas[x][y - 1] : 0;
+            }
+        }
+    }
+
+    
+    /**
+     * @description Open a door
+     * @memberOf Wolf.Doors
+     * @param {object} doors The door object.
+     */
+    function open(door) {
+        if (door.action == Wolf.dr_open) {
+            door.ticcount = 0;        // reset opened time
+        } else {
+            door.action = Wolf.dr_opening;    // start opening it
+        }
+    }
+
+    /**
+     * @description Change the state of a door
+     * @private
+     * @param {object} level The level object.
+     * @param {object} player The player object.
+     * @param {object} doors The door object.
+     */
+    function changeDoorState(level, player, door) {
+        if (door.action < Wolf.dr_opening ) {
+            open(door);
+        } else if (door.action == Wolf.dr_open && canCloseDoor(level, player, door.tile.x, door.tile.y, door.vertical)) {
+            // !@# for the iphone with automatic using, don't allow any door close actions        
+            // Door->action = dr_closing;
+            // Door->ticcount = DOOR_FULLOPEN;
+        }
+    }
+
+    
+    function canCloseDoor(level, player, x, y, vert ) {
+        var n,
+            tileX = Wolf.POS2TILE(player.position.x),
+            tileY = Wolf.POS2TILE(player.position.y),
+            guard;
+
+        if (tileX == x && tileY == y ) {
+            return false;
+        }
+
+        if (vert) {
+            if (tileY == y) {
+                if (Wolf.POS2TILE(player.position.x + Wolf.CLOSEWALL) == x) {
+                    return false;
+                }
+                if (Wolf.POS2TILE(player.position.x - Wolf.CLOSEWALL) == x) {
+                    return false;
+                }
+            }
+
+            for (n = 0; n<level.state.numGuards;++n) {
+                guard = level.state.guards[n];
+                if (guard.tile.x == x && guard.tile.y == y ) {
+                    return false; // guard in door
+                }
+                if (guard.tile.x == x - 1 && guard.tile.y == y && Wolf.POS2TILE(guard.x + Wolf.CLOSEWALL) == x) {
+                    return false; // guard in door
+                }
+                if (guard.tile.x == x + 1 && guard.tile.y == y && Wolf.POS2TILE(guard.x - Wolf.CLOSEWALL) == x) {
+                    return false; // guard in door
+                }
+            }
+        } else {
+            if (tileX == x) {
+                if (Wolf.POS2TILE(player.position.y + Wolf.CLOSEWALL) == y) {
+                    return false;
+                }
+                if (Wolf.POS2TILE(player.position.y - Wolf.CLOSEWALL) == y) {
+                    return false;
+                }
+            }
+            for (n = 0; n<level.state.numGuards;++n) {
+                var guard = level.state.guards[n];
+                if (guard.tile.x == x && guard.tile.y == y ) {
+                    return false; // guard in door
+                }
+                if (guard.tile.x == x && guard.tile.y == y - 1 && Wolf.POS2TILE(guard.y + Wolf.CLOSEWALL) == y) {
+                    return false; // guard in door
+                }
+                if (guard.tile.x == x && guard.tile.y == y + 1 && Wolf.POS2TILE(guard.y - Wolf.CLOSEWALL) == y) {
+                    return false; // guard in door
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * @description Try to use a door with keys that the player has.
+     * @memberOf Wolf.Doors
+     * @param {object} level The level object
+     * @param {object} player The player object
+     * @param {object} door The door object
+     * @returns {boolean} Always returns true.
+     */
+    function tryUse(level, player, door ) {
+        switch (door.type) {
+            case Wolf.DOOR_VERT:
+            case Wolf.DOOR_HORIZ:
+            case Wolf.DOOR_E_VERT:
+            case Wolf.DOOR_E_HORIZ:
+                changeDoorState(level, player, door); // does not require key!
+                break;
+
+            case Wolf.DOOR_G_VERT:
+            case Wolf.DOOR_G_HORIZ:
+                if (player.items & Wolf.ITEM_KEY_1) {
+                    changeDoorState(level, player, door);
+                } else {
+                    Wolf.Game.notify("You need a gold key");
+                }
+                break;
+
+            case Wolf.DOOR_S_VERT:
+            case Wolf.DOOR_S_HORIZ:
+                if (player.items & Wolf.ITEM_KEY_2) {
+                    changeDoorState(level, player, door);
+                } else {
+                    Wolf.Game.notify("You need a silver key");
+                }
+                break;
+        }
+        return true; // FIXME
+    }
+    
+    return {
+        reset : reset,
+        spawn : spawn,
+        opened : opened,
+        open : open,
+        tryUse : tryUse,
+        process : process,
+        setAreas : setAreas
+    };
+
+})();
diff --git a/js/episodes.js b/js/episodes.js
new file mode 100644 (file)
index 0000000..6d68532
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+* ===========================================================================
+* 
+* Wolf3D Browser Version GPL Source Code
+* Copyright (C) 2012 id Software LLC, a ZeniMax Media company. 
+* 
+* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").  
+* 
+* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+* 
+* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+* 
+* You should have received a copy of the GNU General Public License version 2
+* along with Wolf3D Browser Source Code.  If not, see <http://www.gnu.org/licenses/>.
+* 
+* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
+* 
+* ===========================================================================
+*/
+
+/** 
+ * @namespace 
+ * @description Episode data
+ */
+Wolf.Episodes = [
+    { 
+        name : "Escape from Wolfenstein",
+        enabled : true,
+        levels : [
+            { file : "maps/w00.map",    partime : 1.5 },
+            { file : "maps/w01.map",    partime : 2.0 },
+            { file : "maps/w02.map",    partime : 2.0 },
+            { file : "maps/w03.map",    partime : 3.5 },
+            { file : "maps/w04.map",    partime : 3.0 },
+            { file : "maps/w05.map",    partime : 3.0 },
+            { file : "maps/w06.map",    partime : 2.5 },
+            { file : "maps/w07.map",    partime : 2.5 },
+            { file : "maps/w08.map",    partime : 0.0 },
+            { file : "maps/w09.map",    partime : 0.0,  secret : true }
+        ]
+    },{ 
+        name : "Operation: Eisenfaust",
+        enabled : true,
+        levels : [
+            { file : "maps/w10.map",    partime : 1.5 },
+            { file : "maps/w11.map",    partime : 3.5 },
+            { file : "maps/w12.map",    partime : 3.0 },
+            { file : "maps/w13.map",    partime : 2.0 },
+            { file : "maps/w14.map",    partime : 4.0 },
+            { file : "maps/w15.map",    partime : 6.0 },
+            { file : "maps/w16.map",    partime : 1.0 },
+            { file : "maps/w17.map",    partime : 3.0 },
+            { file : "maps/w18.map",    partime : 0.0 },
+            { file : "maps/w19.map",    partime : 0.0,  secret : true }
+        ]
+    },{ 
+        name : "Die, Fuhrer, Die!",
+        enabled : true,
+        levels : [
+            { file : "maps/w20.map",    partime : 1.5 },
+            { file : "maps/w21.map",    partime : 1.5 },
+            { file : "maps/w22.map",    partime : 2.5 },
+            { file : "maps/w23.map",    partime : 2.5 },
+            { file : "maps/w24.map",    partime : 3.5 },
+            { file : "maps/w25.map",    partime : 2.5 },
+            { file : "maps/w26.map",    partime : 2.0 },
+            { file : "maps/w27.map",    partime : 6.0 },
+            { file : "maps/w28.map",    partime : 0.0 },
+            { file : "maps/w29.map",    partime : 0.0,  secret : true }
+        ]
+    }
+];
+
diff --git a/js/file.js b/js/file.js
new file mode 100644 (file)
index 0000000..3315f38
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+* ===========================================================================
+* 
+* Wolf3D Browser Version GPL Source Code
+* Copyright (C) 2012 id Software LLC, a ZeniMax Media company. 
+* 
+* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").  
+* 
+* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+* 
+* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+* 
+* You should have received a copy of the GNU General Public License version 2
+* along with Wolf3D Browser Source Code.  If not, see <http://www.gnu.org/licenses/>.
+* 
+* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
+* 
+* ===========================================================================
+*/
+
+/** 
+ * @namespace 
+ * @description Binary file reading
+ */
+Wolf.File = (function() {
+
+    
+    /**
+     * @description Open a file from URL 
+     * @memberOf Wolf.File
+     * @param {string} url The URL to open
+     * @param {function} callback Is called when file has been loaded. Second argument is file obj.
+     */
+    function openURL(url, callback) {
+        var xhr = new XMLHttpRequest();
+        
+        xhr.onreadystatechange = function() {
+            if (xhr.readyState == 4) {
+                if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 0) {
+                    callback(null, {
+                        data : xhr.responseText,
+                        size : xhr.responseText.length,
+                        position : 0
+                    });
+                } else {
+                    callback(new Error("Server returned HTTP status: " + xhr.status));
+                }
+            }
+        }
+        xhr.open("GET", url, true);
+        xhr.overrideMimeType('text/plain; charset=x-user-defined');
+        xhr.send(null);
+    }
+    
+       
+       function atob(str) {
+               str = str.replace(/=+$/, "");
+               var b64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
+                       a, b, c, b1, b2, b3, b4, 
+                       chr = String.fromCharCode,
+                       out = [];
+               for (var i=0,len=str.length;i<len;) {
+                       b1 = b64chars.indexOf(str.charAt(i++));
+                       b2 = b64chars.indexOf(str.charAt(i++));
+                       b3 = b64chars.indexOf(str.charAt(i++));
+                       b4 = b64chars.indexOf(str.charAt(i++));
+
+                       a = ((b1 & 0x3F) << 2) | ((b2 >> 4) & 0x3);
+                       b = ((b2 & 0xF) << 4) | ((b3 >> 2) & 0xF);
+                       c = ((b3 & 0x3) << 6) | (b4 & 0x3F);
+
+                       out.push(chr(a), chr(b), chr(c));
+               }
+               return out.join("");
+       }
+       
+    /**
+     * @description Open a file from base64 filetable
+     * @memberOf Wolf.File
+     * @param {string} filename The name of the file to open
+     * @param {object} files The filetable
+     * @param {function} callback Is called when file has been loaded. Second argument is file obj.
+     */
+    function open(filename, files, callback) {
+        var b64data = files[filename];
+        if (b64data) {
+            var data = atob(b64data);
+            callback(null, {
+                data : data,
+                size : data.length,
+                position : 0
+            });
+        } else {
+            callback(new Error("File not found: " + filename));
+        }
+    }
+
+    /**
+     * @description Read an unsigned 8-bit integer from a file and advance the file position.
+     * @memberOf Wolf.File
+     * @param {object} f The file
+     * @returns {number} 
+     */
+    function readUInt8(f) {
+        var b = f.data.charCodeAt(f.position) & 0xFF
+        f.position++;
+        return b;
+    }
+
+    /**
+     * @description Read a signed 8-bit integer from a file and advance the file position.
+     * @memberOf Wolf.File
+     * @param {object} f The file
+     * @returns {number} 
+     */
+    function readInt8(f) {
+        var v = readUInt8(f);
+        return v > 127 ? v - 256 : v;
+    }
+
+    /**
+     * @description Read an unsigned 16-bit integer from a file and advance the file position.
+     * @memberOf Wolf.File
+     * @param {object} f The file
+     * @returns {number} 
+     */
+       function readUInt16(f) {
+               var v = readUInt8(f) + (readUInt8(f) << 8);
+               return (v < 0) ? v + 0x10000 : v;
+       }
+    
+    /**
+     * @description Read a signed 16-bit integer from a file and advance the file position.
+     * @memberOf Wolf.File
+     * @param {object} f The file
+     * @returns {number} 
+     */
+       function readInt16(f) {
+               var v = readUInt16(f);
+        return (v > 0x7fff) ? v - 0x10000 : v;
+       }
+    
+    /**
+     * @description Read an unsigned 32-bit integer from a file and advance the file position.
+     * @memberOf Wolf.File
+     * @param {object} f The file
+     * @returns {number} 
+     */
+       function readUInt32(f) {
+        var b0 = readUInt8(f),
+            b1 = readUInt8(f),
+            b2 = readUInt8(f),
+            b3 = readUInt8(f),
+            v = ((((b3 << 8) + b2) << 8) + b1 << 8) + b0;
+               return (v < 0) ? v + 0x100000000 : v;
+       }
+    
+    /**
+     * @description Read a signed 32-bit int from a file and advance the file position.
+     * @memberOf Wolf.File
+     * @param {object} f The file
+     * @returns {number} 
+     */
+       function readInt32(f) {
+               var v = readUInt32(f);
+        return (v > 0x7fffffff) ? v - 0x100000000 : v;
+       }
+    
+    /**
+     * @description Read a string from a file and advance the file position.
+     * @memberOf Wolf.File
+     * @param {object} f The file
+     * @param {number} length The length of the string
+     * @returns {string} 
+     */
+    function readString(f, length) {
+        var str = f.data.substr(f.position, length);
+        f.position += length;
+        return str;
+    }
+    
+    /**
+     * @description Read an array of bytes a file and advance the file position.
+     * @memberOf Wolf.File
+     * @param {object} f The file
+     * @param {number} num The number of bytes to read
+     * @returns {array} 
+     */
+    function readBytes(f, num) {
+        var b = [];
+        for (var i=0;i<num;i++) {
+            b[i] = f.data.charCodeAt(f.position+i) & 0xFF;
+        }
+        f.position += num;
+        return b;
+    }
+
+    return {
+        open : open,
+        readInt8 : readInt8,
+        readUInt8 : readUInt8,
+        readInt16 : readInt16,
+        readUInt16 : readUInt16,
+        readInt32 : readInt32,
+        readUInt32 : readUInt32,
+        readBytes : readBytes,
+        readString : readString
+    };
+
+})();
diff --git a/js/game.js b/js/game.js
new file mode 100644 (file)
index 0000000..0c6375a
--- /dev/null
@@ -0,0 +1,1351 @@
+/*
+* ===========================================================================
+* 
+* Wolf3D Browser Version GPL Source Code
+* Copyright (C) 2012 id Software LLC, a ZeniMax Media company. 
+* 
+* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").  
+* 
+* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+* 
+* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+* 
+* You should have received a copy of the GNU General Public License version 2
+* along with Wolf3D Browser Source Code.  If not, see <http://www.gnu.org/licenses/>.
+* 
+* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
+* 
+* ===========================================================================
+*/
+
+/** 
+ * @namespace 
+ * @description Game management
+ */
+Wolf.Game = (function() {
+    Wolf.setConsts({
+        BUTTON_ATTACK   : 1,
+        BUTTON_USE      : 2,
+        BUTTON_ANY      : 128,            // any key whatsoever
+
+        BASEMOVE        : 35,
+        RUNMOVE         : 70,
+        MOVESCALE       : 150,
+        BACKMOVESCALE   : 100,
+        MAXMOUSETURN    : 10,
+        TURNANGLESCALE  : 300,
+        MOUSEDEADBAND   : 0.2,
+        
+        gd_baby         : 0,
+        gd_easy         : 1,
+        gd_medium       : 2,
+        gd_hard         : 3
+    });
+
+    var rendering = false,
+        playing = false,
+        fsInit = false,
+        hndRender = 0,
+        hndCycle = 0,
+        hndFps = 0,
+        lastFPSTime = 0,
+        lastFrame = 0,
+        frameNum = 0,
+        cycleNum = 0,
+        mouseEnabled = false,
+        paused = false,
+        intermissionAnim = 0,
+        currentGame = null,
+        levelMusic,
+        processAI = true,
+        keyInputActive = false,
+        preloadTextures = {},
+        preloadSprites = {},
+
+        controls = {
+            up          : ["UP"],
+            left        : ["LEFT"],
+            down        : ["DOWN"],
+            right       : ["RIGHT"],
+            run         : ["SHIFT"],
+            attack      : ["X"],
+            use         : ["SPACE"],
+            strafe      : ["Z"],
+            weapon1     : ["1"],
+            weapon2     : ["2"],
+            weapon3     : ["3"],
+            weapon4     : ["4"]
+        },
+        ticsPerSecond = 70,
+        lastTimeCount = 0;
+        
+    /**
+     * @description Build the movement, angles, and buttons for a frame of action:
+     *   Player.angle
+     *   Player.cmd.buttons
+     *   Player.cmd.forwardMove
+     *   Player.cmd.sideMove
+     * @private 
+     * @param {object} player The player object.
+     * @param {number} tics The number of tics since last frame.
+     */
+    function updatePlayerControls(player, tics) {
+        var moveValue,
+            running = false,
+            strafing = false,
+            leftKey = false,
+            rightKey = false,
+            downKey = false,
+            upKey = false,
+            changeWeapon = -1,
+            mouseMovement,
+            mouseCoords;
+       
+        player.cmd.buttons = 0;
+        player.cmd.forwardMove = 0;
+        player.cmd.sideMove = 0;
+        
+        leftKey = Wolf.Input.checkKeys(controls.left);
+        rightKey = Wolf.Input.checkKeys(controls.right);
+        downKey = Wolf.Input.checkKeys(controls.down);
+        upKey = Wolf.Input.checkKeys(controls.up);
+        
+        running = Wolf.Input.checkKeys(controls.run);
+        strafing = Wolf.Input.checkKeys(controls.strafe);
+        moveValue = (running ? Wolf.RUNMOVE : Wolf.BASEMOVE);
+       
+        if (Wolf.Input.checkKeys(controls.attack) || (mouseEnabled && Wolf.Input.leftMouseDown())) {
+            player.cmd.buttons |= Wolf.BUTTON_ATTACK;
+        }
+       
+        if (mouseEnabled && Wolf.Input.rightMouseDown()) {
+            if (mouseCoords = Wolf.Input.getMouseCoords()) {
+                player.cmd.forwardMove += - (mouseCoords.y < 0 ? Wolf.MOVESCALE : Wolf.BACKMOVESCALE) * moveValue * mouseCoords.y;
+            }
+        } else if (!(upKey && downKey)) {
+            if (upKey) {
+                player.cmd.forwardMove += moveValue * Wolf.MOVESCALE;
+            }
+            if (downKey) {
+                player.cmd.forwardMove += -moveValue * Wolf.BACKMOVESCALE;
+            } 
+        }
+
+        if (mouseEnabled && Wolf.Input.isPointerLocked()) {
+            mouseMovement = Wolf.Input.getMouseMovement();
+            player.angle -= (mouseMovement.x * Wolf.TURNANGLESCALE * tics)>>0;
+        } else {
+            if (leftKey) {
+                if (strafing) {
+                    player.cmd.sideMove += -moveValue * Wolf.MOVESCALE;
+                } else {
+                    player.angle += Wolf.TURNANGLESCALE * tics;
+                }
+            }
+            if (rightKey) {
+                if (strafing) {
+                    player.cmd.sideMove += moveValue * Wolf.MOVESCALE;
+                } else {
+                    player.angle -= Wolf.TURNANGLESCALE * tics;
+                }
+            }
+            
+            if (mouseEnabled && (mouseCoords = Wolf.Input.getMouseCoords())) {
+                if (Math.abs(mouseCoords.x) > Wolf.MOUSEDEADBAND) {
+                    player.angle -= (Wolf.TURNANGLESCALE * tics * (mouseCoords.x + (mouseCoords.x < 0 ? 1 : -1) * Wolf.MOUSEDEADBAND))>>0;
+                }
+            }
+        }
+        
+        // change weapon?
+        if (Wolf.Input.checkKeys(controls.weapon1) && player.items & Wolf.ITEM_WEAPON_1) {
+            changeWeapon = Wolf.WEAPON_KNIFE;
+        } else if (Wolf.Input.checkKeys(controls.weapon2) && player.items & Wolf.ITEM_WEAPON_2 && player.ammo[Wolf.AMMO_BULLETS]) {
+            changeWeapon = Wolf.WEAPON_PISTOL;
+        } else if (Wolf.Input.checkKeys(controls.weapon3) && player.items & Wolf.ITEM_WEAPON_3 && player.ammo[Wolf.AMMO_BULLETS]) {
+            changeWeapon = Wolf.WEAPON_AUTO;
+        } else if (Wolf.Input.checkKeys(controls.weapon4) && player.items & Wolf.ITEM_WEAPON_4 && player.ammo[Wolf.AMMO_BULLETS]) {
+            changeWeapon = Wolf.WEAPON_CHAIN;
+        }
+        if (changeWeapon > -1) {
+            player.previousWeapon = Wolf.WEAPON_KNIFE;
+            player.weapon = player.pendingWeapon = changeWeapon;
+        }
+        
+        if (Wolf.Input.checkKeys(controls.use)) {
+            player.cmd.buttons |= Wolf.BUTTON_USE;
+        }
+    }
+
+    /**
+     * @description Initiate the game cycle to process player and world logic
+     * @private
+     * @param {object} game The game object
+     */
+    function startGameCycle(game) {
+        var deathTics = 0,
+            deathTicsMax = ticsPerSecond * 2;
+            
+        // cancel existing game cycle
+        if (hndCycle) {
+            clearTimeout(hndCycle);
+            hndCycle = 0;
+        }
+
+        function nextCycle() {
+            if (!playing) {
+                return;
+            }
+        
+            hndCycle = setTimeout(nextCycle, 1000 / 30);
+            cycleNum++;
+
+            if (paused) {
+                return;
+            }
+            
+            var player = game.player,
+                level = game.level,
+                lives, score,
+                tics = calcTics();
+            
+            if (player.playstate != Wolf.ex_dead) {
+                updatePlayerControls(player, tics);
+        
+                player.angle = Wolf.Math.normalizeAngle(player.angle);
+        
+                Wolf.Player.process(game, player, tics);
+                if (processAI) {
+                    Wolf.Actors.process(game, tics);
+                }
+                Wolf.PushWall.process(level, tics);
+                Wolf.Doors.process(level, player, tics);
+            } else {
+           
+                if (died(game, tics)) {
+                    deathTics += tics;
+                    if (deathTics >= deathTicsMax) {
+                        deathTics = 0;
+                        $("#game .renderer .death").css("display", "none");
+
+                        if (game.player.lives > 0) {
+                            lives = game.player.lives;
+                            score = game.player.startScore;
+                            game.level = Wolf.Level.reload(level);
+                            Wolf.Level.scanInfoPlane(game.level, game.skill); // Spawn items/guards
+                            game.player = Wolf.Player.spawn(game.level.spawn, game.level, game.skill);
+                            game.player.lives = lives - 1;
+                            game.player.score = score;
+                            game.player.startScore = score;
+                            game.level.state.startTime = (new Date).getTime();
+                            game.level.state.elapsedTime = 0;
+                        } else {
+                            gameOver(game);
+                            return;
+                        }
+                    } else {
+                        $("#game .renderer .death").css({
+                            display : "block",
+                            backgroundColor : "rgba(255,0,0," + (deathTics / deathTicsMax) + ")"
+                        });
+                    }
+                }
+            }
+            Wolf.Sprites.clean(level);
+            updateHUD(game, tics);
+           
+        }
+        
+        lastTimeCount = (new Date).getTime();
+        nextCycle();
+    }
+    
+    
+    function died(game, tics) {
+        var fangle,
+            dx, dy,
+            iangle, curangle,
+            clockwise, 
+            counter,
+            change,
+            player = game.player,
+            killer = player.lastAttacker;
+
+        //gamestate.weapon = -1;                       // take away weapon
+        //SD_PlaySound (PLAYERDEATHSND);
+
+        // swing around to face attacker
+
+        dx = killer.x - player.position.x;
+        dy = player.position.y - killer.y;
+
+        fangle = -Math.atan2(dy,dx);                   // returns -pi to pi
+        if (fangle < 0) {
+            fangle = Math.PI * 2 + fangle;
+        }
+
+        iangle = Math.round(fangle / (Math.PI * 2) * Wolf.ANGLES);
+
+        curangle = Wolf.FINE2DEG(player.angle);
+
+        if (curangle > iangle) {
+            counter = curangle - iangle;
+            clockwise = Wolf.ANGLES - curangle + iangle;
+        } else {
+            clockwise = iangle - curangle;
+            counter = curangle + Wolf.ANGLES - iangle;
+        }
+
+        if (clockwise < counter) {
+            // rotate clockwise
+            if (curangle > iangle) {
+                curangle -= Wolf.ANGLES;
+            }
+            if (curangle == iangle) {
+                return true;
+            } else {
+                change = tics * Wolf.DEATHROTATE;
+                if (curangle + change > iangle) {
+                    change = iangle - curangle;
+                }
+                curangle += change;
+                if (curangle >= Wolf.ANGLES) {
+                    curangle -= Wolf.ANGLES;
+                }
+                player.angle = Wolf.DEG2FINE(curangle);
+            }
+        } else {
+            // rotate counterclockwise
+            if (curangle < iangle) {
+                curangle += Wolf.ANGLES;
+            }
+            if (curangle == iangle) {
+                return true;
+            } else {
+                change = -tics * Wolf.DEATHROTATE;
+                if (curangle + change < iangle) {
+                    change = iangle - curangle;
+                }
+                curangle += change;
+                if (curangle < 0) {
+                    curangle += Wolf.ANGLES;
+                }
+                player.angle = Wolf.DEG2FINE(curangle);
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * @description Game over. No more lives.
+     * @private
+     * @param {object} game The game object
+     */
+    function gameOver(game) {
+        playing = false;
+        rendering = false;
+        
+        $("#game .renderer").hide();
+        $("#game .fps").hide();
+        $("#game .gameover").show();
+        endGame();
+        
+        function exit() {
+            $(document).off("keydown", progress);
+            $("#game").fadeOut(null, function() {
+                $("#game .gameover").hide();
+                Wolf.Menu.show();
+            });
+        }
+        function progress(e) {
+            if (!$("#game .gameover").is(":visible")) {
+                exit();
+                return;
+            }
+            if (e.keyCode == 13 || e.keyCode == 32) {
+                exit();
+            }
+        }
+        $(document).on("keydown", progress);
+    }
+    
+    
+    function victory(game) {
+        if (game.player.playstate == Wolf.ex_victory) {
+            return;
+        }
+        keyInputActive = false;
+        Wolf.log("Victory!");
+        $("#game .renderer .player-weapon").hide();
+        Wolf.Actors.spawnBJVictory(game.player, game.level, game.skill);
+        game.player.playstate = Wolf.ex_victory;
+    }
+    
+    function endEpisode(game) {
+        Wolf.Game.startIntermission(game);
+    }
+    
+    /**
+     * @description Calculate the number of tics since last time calcTics() was called.
+     *              Accumulates fractions.
+     * @private
+     * @returns {number} The number of tics
+     */
+    function calcTics() {
+        var now = (new Date).getTime(),
+            delta = (now - lastTimeCount) / 1000,
+            tics = Math.floor(ticsPerSecond * delta);
+
+        lastTimeCount += (tics * 1000 / ticsPerSecond) >> 0;
+        
+        return tics;
+    }
+
+    /**
+     * @description Update HUD stats
+     * @private 
+     * @param {string} name The name/class of the player stat (health, ammo, etc.)
+     * @param {number} value The new value
+     */
+    function updateStat(name, value) {
+        var numdivs = $("#game .hud ." + name + " .number");
+        for (var i=numdivs.length-1;i>=0;i--) {
+            if (value == 0 && i < numdivs.length-1) {
+                numdivs[i].style.backgroundPosition = 16 + "px 0";
+            } else {
+                numdivs[i].style.backgroundPosition = - (16 * (value % 10)) + "px 0";
+                value = (value / 10) >> 0;
+            }
+        }
+    }
+    
+    
+    
+    
+    /**
+     * @description Update the HUD
+     * @private 
+     * @param {object} game The game object
+     */
+    function updateHUD(game, tics) {
+        var player = game.player,
+            frame = player.weapon * 4 + player.weaponFrame;
+
+        if (player.playstate == Wolf.ex_dead || player.playstate == Wolf.ex_victory) {
+            $("#game .renderer .player-weapon").css("display", "none");
+        } else {
+            $("#game .renderer .player-weapon").css({
+                display : "block",
+                backgroundPosition : - (frame * Wolf.HUD_WEAPON_WIDTH) + "px 0"
+            });
+        }
+        
+        $("#game .hud .weapon").css({
+            backgroundPosition : - (player.weapon * 96) + "px 0"
+        });
+        
+        $("#game .hud .key1").css({
+            display : (player.items & Wolf.ITEM_KEY_1) ? "block" : "none"
+        });
+        $("#game .hud .key2").css({
+            display : (player.items & Wolf.ITEM_KEY_2) ? "block" : "none"
+        });
+        
+        updateStat("ammo", player.ammo[Wolf.AMMO_BULLETS]);
+        updateStat("health", player.health);
+        updateStat("lives", player.lives);
+        updateStat("score", player.score);
+        updateStat("floor", game.levelNum+1);
+        
+        drawFace(player, tics);
+    }
+    
+    /**
+     * @description Update the game display
+     * @private 
+     * @param {object} game The game object
+     */
+    function updateScreen(game) {
+        var player = game.player,
+            level = game.level,
+            viewport = {
+                x : player.position.x,
+                y : player.position.y,
+                angle : player.angle
+            };
+        
+        var res = Wolf.Raycaster.traceRays(viewport, level);
+        
+        Wolf.Renderer.clear();
+        Wolf.Renderer.draw(viewport, level, res.tracers, res.visibleTiles);
+    }
+    
+
+
+     /**
+     * @description Update BJ face pic
+     * @private 
+     * @param {object} player
+     * @param {number} tics
+     */
+    function drawFace(player, tics) {
+        var pic;
+        // decide on the face
+        player.faceCount += tics;
+        if (player.faceGotGun && player.faceCount > 0) {
+            // gotgun will set facecount to a negative number initially, go back
+            // to normal face with random look after expired.
+            player.faceGotGun = false;
+        }
+        if (player.faceCount > Wolf.Random.rnd()) {
+            player.faceGotGun = player.faceOuch = false;
+            player.faceFrame = Wolf.Random.rnd() >> 6;
+            if( player.faceFrame == 3) {
+                player.faceFrame = 0;
+            }
+            player.faceCount = 0;
+        }
+
+        if (player.health) {
+            if (player.faceGotGun) {
+                pic = 22;
+            } else {
+                var h = player.health;
+                if (h > 100) {
+                    h = 100;
+                }
+                if (h < 0) {
+                    h = 0;
+                }
+                pic = (3*((100-h)/16)>>0) + player.faceFrame;
+                
+                //gsh
+                if ((player.flags & Wolf.FL_GODMODE)) {
+                    pic = 23 + player.faceFrame;
+                }
+            }
+        } else {
+            pic = 21;
+        }
+
+        $("#game .hud .bj").css({
+            backgroundPosition : - (pic * Wolf.HUD_FACE_WIDTH) + "px 0"
+        });
+    }
+
+    
+    /**
+     * @description Update the FPS counter
+     * @private 
+     */
+    function updateFPS() {
+        var now = (new Date).getTime(),
+            dt = (now - lastFPSTime) / 1000,
+            frames = frameNum - lastFrame;
+
+        lastFPSTime = now;
+        lastFrame = frameNum;
+        
+        $("#game .fps").html((frames / dt).toFixed(2));
+    }
+
+    /**
+     * @description Initiate the rendering cycle
+     * @private 
+     * @param {object} game The game object
+     */
+    function startRenderCycle(game) {
+        // cancel existing render cycle
+        if (hndRender) {
+            cancelAnimationFrame(hndRender);
+            hndRender = 0;
+        }
+        
+        /*
+        if (!hndFps) {
+            hndFps = setInterval(updateFPS, 1000);
+        }
+        $("#game .fps").show();
+        */
+        
+        Wolf.Renderer.init();
+        
+        $("#game .renderer").show();
+        
+        function nextFrame() {
+            if (!rendering) {
+                return;
+            }
+            if (!paused) {
+                updateScreen(game);
+            }
+            hndRender = requestAnimationFrame(nextFrame);
+            frameNum++;
+        }
+        rendering = true;
+        nextFrame();
+    }
+
+    
+    /**
+     * @description Start playing the specified level of the specified episode.
+     * @memberOf Wolf.Game
+     * @param {object} game The game object.
+     * @param {number} episodeNum The episode number.
+     * @param {number} levelNum The level number.
+     */
+    function startLevel(game, episodeNum, levelNum) {
+        if (!Wolf.Episodes[episodeNum].enabled) {
+            return;
+        }
+        
+        playing = false;
+        rendering = false;
+        
+        game.episodeNum = episodeNum;
+        game.levelNum = levelNum;
+
+        var episode = Wolf.Episodes[game.episodeNum];
+        
+        Wolf.Level.load(episode.levels[game.levelNum].file, function(error, level) {
+            if (error) {
+                throw error;
+            }
+            
+            $("#game .renderer .floor").css({
+                "background-color" : "rgb("
+                    + level.floor[0] + ","
+                    + level.floor[1] + ","
+                    + level.floor[2] + ")"
+            });
+            
+            $("#game .renderer .ceiling").css({
+                "background-color" : "rgb("
+                    + level.ceiling[0] + ","
+                    + level.ceiling[1] + ","
+                    + level.ceiling[2] + ")"
+            });
+            
+           
+            game.level = level;
+            
+            levelMusic = level.music;
+            
+            Wolf.Level.scanInfoPlane(level, game.skill); // Spawn items/guards
+            
+            /*
+            game.player.position.x = 1944862;
+            game.player.position.y = 2156427;
+            game.player.angle = 8507;
+            */
+            
+            $("#game .loading").show();
+
+            preloadLevelAssets(level, function() {
+                
+                Wolf.Sound.startMusic(level.music);
+                
+                game.player = Wolf.Player.spawn(level.spawn, level, game.skill, game.player);
+                
+                game.player.startScore = game.player.score;
+                
+                level.state.startTime = (new Date).getTime();
+                level.state.elapsedTime = 0;
+
+                playing = true;
+                startGameCycle(game);
+                startRenderCycle(game);
+                Wolf.Input.reset();
+                Wolf.Input.lockPointer();
+                
+                $("#game .loading").hide();
+                $("#game").focus();
+                $("#game .renderer .player-weapon").show();
+                keyInputActive = true;
+            });
+
+        });
+    }
+
+    /**
+     * @description Preload the music and textures for the specified level
+     * @private
+     * @param {object} level The level object
+     * @param {function} callback Called when all files have loaded.
+     */
+    function preloadLevelAssets(level, callback) {
+        var files = [],
+            tx, ty, texture, x, y, f, i, numFiles,
+            texturePath = "art/walls-shaded/" + Wolf.TEXTURERESOLUTION + "/",
+            spritePath = "art/sprites/" + Wolf.TEXTURERESOLUTION + "/";
+
+        function addTexture(texture) {
+            if (texture > 0) {
+                if (texture % 2 == 0) {
+                    texture--;
+                }
+                f = texturePath + "w_" + texture + ".png";
+                if (!preloadTextures[f]) {
+                    files.push(f);
+                    preloadTextures[f] = true;
+                }
+            }
+        }
+       
+        for (x=0;x<64;++x) {
+            for (y=0;y<64;++y) {
+                addTexture(level.wallTexX[x][y]);
+                addTexture(level.wallTexY[x][y]);
+            }
+        }
+        
+        // static sprites
+        f = spritePath + "002_053.png";
+        if (!preloadSprites[f]) {
+            files.push(f);
+            preloadSprites[f] = true
+        }
+        
+        /*
+        for (i=0;i<level.state.guards.length;++i) {
+            texture = level.state.guards[i].sprite;
+            if (texture) {
+                f = spritePath + Wolf.Sprites.getTexture(texture).sheet;
+                if (!preloadSprites[f]) {
+                    files.push(f);
+                    preloadSprites[f] = true;
+                }
+            }
+        }
+        */
+        
+        for (i=0;i<files.length;++i) {
+            files[i] = "preload!timeout=5!" + files[i];
+        }
+        
+        if (files.length) {
+            Modernizr.load({
+                load : files,
+                complete : callback
+            });
+        } else {
+            callback();
+        }
+    }
+    
+   
+    /**
+     * @description Start a new game with the specified skill level.
+     * @memberOf Wolf.Game
+     * @param {number} skill The difficulty level.
+     */
+    function startGame(skill) {
+        if (isPlaying()) {
+            endGame();
+            levelMusic = null;
+            Wolf.Sound.stopAllSounds();
+        }
+        
+        $("#game .renderer .death").hide();
+        $("#game .renderer .damage-flash").hide();
+        $("#game .renderer .bonus-flash").hide();
+        $("#game").show();
+        
+        var game = {
+            episode : -1,
+            level : -1,
+            skill : skill,
+            killRatios : [],
+            secretRatios : [],
+            treasureRatios : [],
+            totalTime : 0
+        };
+        currentGame = game; // for debugging only
+        
+        return game;
+    }
+    
+    function endGame() {
+        // cancel game cycle
+        if (hndCycle) {
+            clearTimeout(hndCycle);
+            hndCycle = 0;
+        }
+        // cancel render cycle
+        if (hndRender) {
+            cancelAnimationFrame(hndRender);
+            hndRender = 0;
+        }
+        playing = false;
+        rendering = false;
+        Wolf.Renderer.reset();
+        if (paused) {
+            togglePause();
+        }
+    }
+    
+    function startVictoryText(game) {
+        endGame();
+        $("#game").fadeOut(null, function() {
+            var name = "victory" + (game.episodeNum+1), 
+                num = (game.episodeNum == 2) ? 1 : 2;
+                
+            Wolf.Menu.showText(name, num, function() {
+                Wolf.Menu.show("main");
+            });
+        });
+    }
+    
+    /**
+     * @description Start the post-level intermission.
+     * @memberOf Wolf.Game
+     * @param {object} game The game object.
+     */
+    function startIntermission(game, delay) {
+        var episode = Wolf.Episodes[game.episodeNum],
+            parTime = episode.levels[game.levelNum].partime * 60,
+            bonus = 0,
+            parBonusAmount = 500,
+            ratioBonusAmount = 10000,
+            levelState = game.level.state,
+            killRatio = levelState.totalMonsters ? ((levelState.killedMonsters / levelState.totalMonsters * 100) >> 0) : 0,
+            secretRatio = levelState.totalSecrets ? ((levelState.foundSecrets / levelState.totalSecrets * 100) >> 0) : 0,
+            treasureRatio = levelState.totalTreasure ? ((levelState.foundTreasure / levelState.totalTreasure * 100) >> 0) : 0,
+            time = levelState.elapsedTime + ((new Date).getTime() - levelState.startTime),
+            totalTime, i,  
+            avgKill = 0, avgSecret = 0, avgTreasure = 0;
+            
+        playing = false;
+
+        Wolf.Sound.startMusic("music/URAHERO.ogg");
+        
+        $("#game .renderer").hide();
+        $("#game .fps").hide();
+        $("#game .intermission .digit").hide();
+        $("#game .intermission").show();
+        
+        $("#game .intermission .background").hide();
+        $("#game .intermission .background-secret").hide();
+        $("#game .intermission .background-victory").hide();
+        $("#game .intermission .stat").hide();
+        $("#game .intermission .victory-stat").hide();
+        $("#game .intermission .bj").hide();
+        
+        // 99 mins max
+        time = Math.min(99*60, Math.round(time / 1000));
+        
+        killRatio = Math.min(killRatio, 100);
+        secretRatio = Math.min(secretRatio, 100);
+        treasureRatio = Math.min(treasureRatio, 100);
+
+        game.killRatios.push(killRatio);
+        game.secretRatios.push(secretRatio);
+        game.treasureRatios.push(treasureRatio);
+        game.totalTime += time;
+       
+        // secret level
+        if (game.levelNum == 9) { 
+            $("#game .intermission .background-secret").show();
+            $("#game .intermission .bj").show();
+            bonus = 15000;
+            
+        // boss level
+        } else if (game.levelNum == 8) { 
+            $("#game .intermission .background-victory").show();
+            $("#game .intermission .victory-stat").show();
+            
+            totalTime = Math.min(99*60, game.totalTime);
+            for (i=0;i<game.killRatios.length;i++) {
+                avgKill += game.killRatios[i];
+            }
+            for (i=0;i<game.secretRatios.length;i++) {
+                avgSecret += game.secretRatios[i];
+            }
+            for (i=0;i<game.treasureRatios.length;i++) {
+                avgTreasure += game.treasureRatios[i];
+            }
+            avgKill = Math.round(avgKill / game.killRatios.length);
+            avgSecret = Math.round(avgSecret / game.secretRatios.length);
+            avgTreasure = Math.round(avgTreasure / game.treasureRatios.length);
+            
+            setIntermissionNumber("total-time-minutes", (totalTime / 60) >> 0, true);
+            setIntermissionNumber("total-time-seconds", ((totalTime / 60) % 1) * 60, true);
+            
+            setIntermissionNumber("avg-kill-ratio", avgKill, false);
+            setIntermissionNumber("avg-secret-ratio", avgSecret, false);
+            setIntermissionNumber("avg-treasure-ratio", avgTreasure, false);
+
+        // regular level
+        } else {
+            $("#game .intermission .background").show();
+            $("#game .intermission .bj").show();
+            $("#game .intermission .stat").show();
+            
+           
+            if (parTime && parTime > time) {
+                bonus += (parTime - time) * parBonusAmount;
+            }
+            if (killRatio == 100) {
+                bonus += ratioBonusAmount;
+            }
+            if (secretRatio == 100) {
+                bonus += ratioBonusAmount;
+            }
+            if (treasureRatio == 100) {
+                bonus += ratioBonusAmount;
+            }
+
+            time = time / 60;
+            parTime = parTime / 60;
+            
+            setIntermissionNumber("floor", game.levelNum + 1, false);
+            
+            setIntermissionNumber("bonus", bonus, false);
+            
+            setIntermissionNumber("time-minutes", time >> 0, true);
+            setIntermissionNumber("time-seconds", (time % 1) * 60, true);
+
+            setIntermissionNumber("par-minutes", parTime >> 0, true);
+            setIntermissionNumber("par-seconds", (parTime % 1) * 60, true);
+            
+            setIntermissionNumber("kill-ratio", killRatio, false);
+            setIntermissionNumber("secret-ratio", secretRatio, false);
+            setIntermissionNumber("treasure-ratio", treasureRatio, false);
+            
+        }
+        
+        function anim() {
+            var now = (new Date).getTime(),
+                bjFrame = Math.floor(now / 500) % 2;
+
+            $("#game .intermission .bj").css({
+                backgroundPosition : - (162 * bjFrame) + "px 0px"
+            });
+            intermissionAnim = requestAnimationFrame(anim);
+        }
+        
+        if (game.levelNum != 8) {
+            if (!intermissionAnim) {
+                anim();
+            }
+        }
+        
+        function exitIntermission() {
+            if (intermissionAnim) {
+                cancelAnimationFrame(intermissionAnim);
+                intermissionAnim = 0;
+            }
+            $(document).off("keydown", progress);
+            $("#game .intermission").hide();
+        }
+        
+        function progress(e) {
+            var nextLevel;
+            if (!$("#game .intermission").is(":visible")) {
+                exitIntermission();
+                return;
+            }
+            if (e.keyCode == 13 || e.keyCode == 32) {
+                exitIntermission();
+                if (game.player.playstate == Wolf.ex_secretlevel) {
+                    nextLevel = 9;
+                } else {
+                    if (game.levelNum == 8) { // Level was boss level - end of episode.
+                        $("#game").fadeOut(1000, function() {
+                            startVictoryText(game);
+                        });
+                        return;
+                    } else if (game.levelNum == 9) { // Level was secret level - go to previous level + 1
+                        // yuck
+                        switch (game.episodeNum) {
+                            case 0: nextLevel = 1;  break;
+                            case 1: nextLevel = 1; break;
+                            case 2: nextLevel = 7; break;
+                            case 3: nextLevel = 3; break;
+                            case 4: nextLevel = 4; break;
+                            case 5: nextLevel = 3; break;
+                            default: nextLevel = game.levelNum + 1; break;
+                        }                        
+                    } else {
+                        nextLevel = game.levelNum + 1;
+                    }
+                }
+                Wolf.Player.givePoints(game.player, bonus);
+                startLevel(game, game.episodeNum, nextLevel);
+            }
+        }
+        
+        $(document).on("keydown", progress);
+    }
+    
+    
+    /**
+     * @description Update an intermission screen stat.
+     * @private
+     * @param {object} name The name (and CSS class) of the stat
+     * @param {number} value The value.
+     * @param {boolean} zeros If true, leading zeros are displayed.
+     */
+    function setIntermissionNumber(name, value, zeros) {
+        var digits = $("#game .intermission ." + name + " .digit"),
+            i, digit, v;
+        for (i=0;i<10;i++) {
+            digits.removeClass("num-" + i);
+        }
+        value = value >> 0;
+        v = value;
+        for (i=0;i<digits.length;i++) {
+            digit = v % 10;
+            if (v > 0 || zeros || (value == 0 && i == 0)) {
+                digits.eq(digits.length - 1 - i).addClass("num-" + digit);
+            }
+            v = (v / 10) >> 0;
+        }
+        digits.show();
+    }
+
+    
+    /**
+     * @description Start red damage flash.
+     * @memberOf Wolf.Game
+     */
+    function startDamageFlash() {
+        $("#game .renderer .damage-flash").show().fadeOut(300);
+    }
+    
+    /**
+     * @description Start bonus flash.
+     * @memberOf Wolf.Game
+     */
+    function startBonusFlash() {
+        $("#game .renderer .bonus-flash").show().fadeOut(300);
+    }
+    
+    /**
+     * @description Show a notification.
+     * @memberOf Wolf.Game
+     * @param {string} text The notification
+     */
+    function notify(text) {
+        Wolf.log(text);
+    }
+    
+    /**
+     * @description Query fullscreen.
+     * @memberOf Wolf.Game
+     * @returns boolean True if browser is in fullscreen mode, otherwise false.
+     */
+    function isFullscreen() {
+        if ("webkitIsFullScreen" in document) {
+            return document.webkitIsFullScreen;
+        } else if ("mozFullScreen" in document) {
+            return document.mozFullScreen;
+        } else if ($("#main").data("scale") > 1) {
+            return true;
+        }
+        return false;
+    }
+    
+    /**
+     * @description Fullscreen event handler.
+     * @private
+     * @param {object} e Event object.
+     */
+    function fullscreenChange(e) {
+        if (isFullscreen()) {
+            enterFullscreen();
+        } else {
+            exitFullscreen();
+        }
+    }
+    
+    /**
+     * @description Toggle the fullscreen state
+     * @private
+     */
+    function toggleFullscreen() {
+        var main = $("#main")[0],
+            ret = false;
+        if (isFullscreen()) {
+            if (document.exitFullscreen) {
+                document.exitFullscreen();
+                return true;
+            } else if (document.webkitCancelFullscreen) {
+                document.webkitCancelFullscreen();
+                return true;
+            } else if (document.mozCancelFullscreen) {
+                document.mozCancelFullscreen();
+                return true;
+            }
+        } else {
+            if (main.requestFullScreenWithKeys) {
+                main.requestFullScreenWithKeys();
+                return true;
+            } else if (main.requestFullScreen) {
+                main.requestFullScreen(true);
+                return true;
+            } else if (main.webkitRequestFullScreen) {
+                main.webkitRequestFullScreen(true);
+                return true;
+            } else if (document.body.mozRequestFullScreenWithKeys) {
+                document.body.mozRequestFullScreenWithKeys();
+                return true;
+            } else if (document.body.mozRequestFullScreen) {
+                document.body.mozRequestFullScreen();
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    
+    /**
+     * @description Scale the game to fit fullscreen mode
+     * @private
+     */
+    function enterFullscreen() {
+        var ratio = window.innerWidth / 640,
+            sliceZoom = Math.floor(Wolf.SLICE_WIDTH * ratio),
+            zoom = sliceZoom / Wolf.SLICE_WIDTH,
+            transform = "scale(" + zoom + ")";
+
+        $("#main").css({
+            "transform" : transform,
+            "-webkit-transform" : transform,
+            "-moz-transform" : transform,
+            "-ms-transform" : transform,
+            "-o-transform" : transform
+        }).data("scale", zoom);
+    }
+    
+    /**
+     * @description Scale the game to fit windowed mode
+     * @private
+     */
+    function exitFullscreen() {
+        $("#main").css({
+            "transform" : "",
+            "-webkit-transform" : "",
+            "-moz-transform" : "",
+            "-ms-transform" : "",
+            "-o-transform" : ""
+        }).data("scale", 1);
+    }
+    
+    
+    /**
+     * @description Initialize the game module
+     * @memberOf Wolf.Game
+     */
+    function init() {
+        $(document)
+            .on("mozfullscreenchange", fullscreenChange)
+            .on("webkitfullscreenchange", fullscreenChange)
+            .on("fullscreenchange", fullscreenChange);
+            
+        Wolf.Input.bindKey("F11", function(e) {
+            if (!keyInputActive) {
+                return;
+            }
+            if (toggleFullscreen()) {
+                e.preventDefault();
+            } else {
+                if (isFullscreen()) {
+                    exitFullscreen();
+                } else {
+                    enterFullscreen();
+                }
+            }
+        });
+        
+        Wolf.Input.bindKey("P", function(e) {
+            if (!keyInputActive) {
+                return;
+            }
+            togglePause();
+        });
+        
+        Wolf.Input.bindKey("ESC", function(e) {
+            if (!keyInputActive) {
+                return;
+            }
+            exitToMenu();
+        });
+       
+        if (!isFullscreen() && (window.fullScreen || (window.innerWidth == screen.width && window.innerHeight == screen.height))) {
+            toggleFullscreen();
+        }
+    }
+    
+    /**
+     * @description Exit to main menu
+     * @memberOf Wolf.Game
+     */
+    function exitToMenu() {
+        if (!paused) {
+            togglePause();
+        }
+        $("#game").hide();
+        keyInputActive = false;
+        Wolf.Menu.show("main");
+    }
+    
+    /**
+     * @description Resume game after coming from menu
+     * @memberOf Wolf.Game
+     */
+    function resume() {
+        $("#game").show();
+        if (paused) {
+            togglePause();
+        }
+        keyInputActive = true;
+        if (levelMusic) {
+            Wolf.Sound.startMusic(levelMusic);
+        }
+    }
+   
+    
+    /**
+     * @description Query the game state
+     * @memberOf Wolf.Game
+     * @returns {boolean} True if the is currently playing
+     */
+    function isPlaying() {
+        return playing;
+    }
+    
+    
+    /**
+     * @description Toggle the pause state.
+     * @private
+     */
+    function togglePause() {
+        paused = !paused;
+        if (paused) {
+            Wolf.Sound.pauseMusic(true);
+        } else {
+            Wolf.Sound.pauseMusic(false);
+            lastTimeCount = (new Date).getTime();
+        }
+        $("#game .renderer div.pause.overlay").toggle(paused);
+    }
+    
+    
+    function enableMouse(enable) {
+        mouseEnabled = enable;
+    }
+    function isMouseEnabled() {
+        return mouseEnabled;
+    }
+
+    function getControls() {
+        var c = {};
+        for (var a in controls) {
+            if (controls.hasOwnProperty(a)) {
+                c[a] = controls[a];
+            }
+        }
+        return c;
+    }
+    
+    function bindControl(action, keys) {
+        controls[action] = keys;
+    }
+    
+    /*
+    function dump() {
+        console.log(currentGame);
+        window.open("data:text/plain," + JSON.stringify(currentGame), "dump");
+    }
+    
+    function debugGodMode(enable) {
+        if (currentGame && currentGame.player) {
+            if (enable) {
+                currentGame.player.flags |= Wolf.FL_GODMODE;
+            } else {
+                currentGame.player.flags &= ~Wolf.FL_GODMODE;
+            }
+            Wolf.log("God mode " + (enable ? "enabled" : "disabled"));
+        }
+    }
+    
+    function debugNoTarget(enable) {
+        if (currentGame && currentGame.player) {
+            if (enable) {
+                currentGame.player.flags |= Wolf.FL_NOTARGET;
+            } else {
+                currentGame.player.flags &= ~Wolf.FL_NOTARGET;
+            }
+            Wolf.log("No target " + (enable ? "enabled" : "disabled"));
+        }
+    }
+    
+    function debugVictory() {
+        if (currentGame && currentGame.player) {
+            Wolf.log("Winning!");
+            Wolf.Game.startIntermission(currentGame);
+        }
+    }
+    
+    function debugEndEpisode() {
+        if (currentGame && currentGame.player) {
+            victory(currentGame);
+        }
+    }
+    
+    function debugToggleAI(enable) {
+        processAI = !!enable;
+    }
+    
+    function debugGiveAll() {
+        if (currentGame && currentGame.player) {
+            Wolf.Player.givePoints(currentGame.player, 10000);
+            Wolf.Player.giveHealth(currentGame.player, 100, 100);
+            Wolf.Player.giveKey(currentGame.player, 0);
+            Wolf.Player.giveKey(currentGame.player, 1);
+            Wolf.Player.giveWeapon(currentGame.player, 2);
+            Wolf.Player.giveWeapon(currentGame.player, 3);
+            Wolf.Player.giveAmmo(currentGame.player, Wolf.AMMO_BULLETS, 99);
+            Wolf.log("Giving keys, weapons, ammo, health and 10000 points");
+        }
+    }
+    */
+   
+    return {
+        startGame : startGame,
+        startLevel : startLevel,
+        startIntermission : startIntermission,
+        startDamageFlash : startDamageFlash,
+        startBonusFlash : startBonusFlash,
+        enableMouse : enableMouse,
+        isMouseEnabled : isMouseEnabled,
+        isPlaying : isPlaying,
+        notify : notify,
+        isFullscreen : isFullscreen,
+        init : init,
+        getControls : getControls,
+        bindControl : bindControl,
+        resume : resume,
+        victory : victory,
+        endEpisode : endEpisode
+        
+        /*
+        dump : dump,
+        debugGodMode : debugGodMode,
+        debugNoTarget : debugNoTarget,
+        debugToggleAI : debugToggleAI,
+        debugGiveAll : debugGiveAll,
+        debugVictory : debugVictory,
+        debugEndEpisode : debugEndEpisode
+        */
+    };
+
+})();
\ No newline at end of file
diff --git a/js/input.js b/js/input.js
new file mode 100644 (file)
index 0000000..fd215c5
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+* ===========================================================================
+* 
+* Wolf3D Browser Version GPL Source Code
+* Copyright (C) 2012 id Software LLC, a ZeniMax Media company. 
+* 
+* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").  
+* 
+* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+* 
+* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+* 
+* You should have received a copy of the GNU General Public License version 2
+* along with Wolf3D Browser Source Code.  If not, see <http://www.gnu.org/licenses/>.
+* 
+* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
+* 
+* ===========================================================================
+*/
+
+/** 
+ * @namespace 
+ * @description Functions for capturing keyboard/mouse input
+ */
+Wolf.Input = (function() {
+
+    var keys,
+        lmbDown = false,
+        rmbDown = false,
+        bindings = [],
+        hasFocus = false,
+        mouseX = -1, mouseY = -1,
+        mouseMoveX = 0, mouseMoveY = 0;
+    
+    function init() {
+        var game = $("#game"),
+            main = $("#main"),
+            renderer = $("#game .renderer");
+        
+        if (!keys) {
+            keys = [];
+            
+            $(document)
+                .on("keydown", function(e) {
+                    e.preventDefault();
+                    
+                    if (!Wolf.Game.isPlaying()) {
+                        return;
+                    }
+                    
+                    keys[e.keyCode] = true;
+                    if (bindings[e.keyCode]) {
+                        for (var i=0,n=bindings[e.keyCode].length;i<n;i++) {
+                            bindings[e.keyCode][i](e);
+                        }
+                    }
+                })
+                .on("keyup", function(e) {
+                    e.preventDefault();
+                    if (!Wolf.Game.isPlaying()) {
+                        return;
+                    }
+                    keys[e.keyCode] = false;
+                })
+                .on("keypress", function(e) {
+                    e.preventDefault();
+                })
+                .on("contextmenu", function(e) {
+                    e.preventDefault();
+                })
+                .on("onrightclick", function(e) {
+                    e.preventDefault();
+                })
+                .on("mousedown", function(e) {
+                    window.focus();
+                    e.preventDefault();
+                })
+                .on("mouseup", function(e) {
+                    e.preventDefault();
+                });
+                
+            $("#game")
+                .on("mousedown", function(e) {
+                    if (hasFocus) {
+                        if (e.which == 1) {
+                            lmbDown = true;
+                        } else if (e.which == 3) {
+                            rmbDown = true;
+                        }
+                    } else {
+                        window.focus();
+                    }
+                    e.preventDefault();
+                })
+                .on("mouseup", function(e) {
+                    if (hasFocus) {
+                        if (e.which == 1) {
+                            lmbDown = false;
+                        } else if (e.which == 3) {
+                            rmbDown = false;
+                        }
+                    }
+                    e.preventDefault();
+                })
+                .on("mousemove", function(e) {
+                    if (!hasFocus) {
+                        return;
+                    }
+                    
+                    if (isPointerLocked()) {
+                        if ("webkitMovementX" in e.originalEvent) {
+                            mouseMoveX += e.originalEvent.webkitMovementX;
+                            mouseMoveY += e.originalEvent.webkitMovementY;
+                        } else if ("mozMovementX" in e.originalEvent) {
+                            mouseMoveX += e.originalEvent.mozMovementX;
+                            mouseMoveY += e.originalEvent.mozMovementY;
+                        } else if ("movementX" in e.originalEvent) {
+                            mouseMoveX += e.originalEvent.movementX;
+                            mouseMoveY += e.originalEvent.movementY;
+                        }
+                    } else {
+                        if (Wolf.Game.isFullscreen()) {
+                            mouseX = e.pageX / window.innerWidth;
+                            mouseY = e.pageY / window.innerHeight;
+                        } else {
+                            var offset = main.offset();
+                            mouseX = (e.pageX - offset.left) / main.width();
+                            mouseY = (e.pageY - offset.top) / main.height();
+                        }
+                    }
+                    e.preventDefault();
+                });
+                
+            // reset keys and mouse if window/tab loses focus
+            $(window).on("blur", function(e) {
+                hasFocus = false;
+                reset();
+            });
+            $(window).on("focus", function(e) {
+                hasFocus = true;
+            });
+        }
+    }
+
+    function reset() {
+        resetMouse();
+        keys = [];
+    }
+    
+    function resetMouse() {
+        lmbDown = false;
+        rmbDown = false;
+        mouseX = mouseY = 0.5;
+    }
+    
+    function bindKey(k, handler) {
+        var keyCode = Wolf.Keys[k];
+        if (!bindings[keyCode]) {
+            bindings[keyCode] = [];
+        }
+        bindings[keyCode].push(handler);
+    }
+    
+    /**
+     * @memberOf Wolf.Input
+     * @description Check if one of the specified keys is pressed.
+     * @param {array} keys Array of key names.
+     * @returns {boolean} True if a key is pressed, otherwise false.
+     */
+    function checkKeys(ckeys) {
+        for (var i=0;i<ckeys.length;i++) {
+            var k = ckeys[i];
+            if (!!keys[Wolf.Keys[k]]) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * @memberOf Wolf.Input
+     * @description Clear status for keys.
+     * @param {array} keys Array of key names.
+     */
+    function clearKeys(ckeys) {
+        for (var i=0;i<ckeys.length;i++) {
+            var k = ckeys[i];
+            keys[Wolf.Keys[k]] = false;
+        }
+        return false;
+    }
+    
+    function leftMouseDown() {
+        return lmbDown;
+    }
+
+    function rightMouseDown() {
+        return rmbDown;
+    }
+    
+    function getMouseCoords() {
+        if (mouseX < 0 || mouseX > 1 || mouseY < 0 || mouseY > 1) {
+            return null;
+        } else {
+            return {
+                x : (mouseX - 0.5) * 2,
+                y : (mouseY - 0.5) * 2
+            };
+        }
+    }
+    
+    function getMouseMovement() {
+        var x = mouseMoveX,
+            y = mouseMoveY;
+        mouseMoveX = 0;
+        mouseMoveY = 0;
+        return {
+            x : x / screen.width,
+            y : y / screen.height
+        };
+    }
+    
+    function getPointer() {
+        var pointer = navigator.pointer ||
+                      navigator.webkitPointer ||
+                      navigator.mozPointer ||
+                      navigator.msPointer ||
+                      navigator.oPointer;
+        return pointer;
+    }
+
+    function isPointerLocked() {
+        var pointer = getPointer();
+        return pointer && pointer.isLocked && pointer.isLocked();
+    }
+    
+    function lockPointer() {
+        var pointer = getPointer();
+        if (!pointer) {
+            return;
+        }
+        
+        if (Wolf.Game.isFullscreen()) {
+            pointer.lock($("#game")[0], 
+                function(e) {
+                    Wolf.log("Pointer locked")
+                }, function(e) {
+                    Wolf.log("Could not lock pointer: " + e);
+                }
+            );
+        }
+    }
+    
+    function unlockPointer() {
+        var pointer = getPointer();
+        if (!pointer) {
+            return;
+        }
+        pointer.unlock($("#game")[0]);
+    }
+    
+    
+    return {
+        init : init,
+        reset : reset,
+        resetMouse : resetMouse,
+        checkKeys : checkKeys,
+        clearKeys : clearKeys,
+        bindKey : bindKey,
+        leftMouseDown : leftMouseDown,
+        rightMouseDown : rightMouseDown,
+        getMouseCoords : getMouseCoords,
+        getMouseMovement : getMouseMovement,
+        isPointerLocked : isPointerLocked,
+        lockPointer : lockPointer,
+        unlockPointer : unlockPointer
+    };
+
+})();
+
+
+Wolf.Keys = {
+    LEFT    : 37,
+    UP      : 38,
+    RIGHT   : 39,
+    DOWN    : 40,
+    ENTER   : 13,
+    SPACE   : 32,
+    SHIFT   : 16,
+    CTRL    : 17,
+    ALT     : 18,
+    ESC     : 27,
+    HOME    : 36,
+    END     : 35,
+    DEL     : 46,
+    INS     : 45,
+    PGUP    : 33,
+    PGDN    : 34,
+    SLASH   : 111,
+    MINUS   : 109,
+    PLUS    : 107,
+    COMMA   : 188,
+    PERIOD  : 190,
+    1       : 49,
+    2       : 50,
+    3       : 51,
+    4       : 52,
+    5       : 53,
+    6       : 54,
+    7       : 55,
+    8       : 56,
+    9       : 57,
+    0       : 58,
+    A       : 65,
+    B       : 66,
+    C       : 67,
+    D       : 68,
+    E       : 69,
+    F       : 70,
+    G       : 71,
+    H       : 72,
+    I       : 73,
+    J       : 74,
+    K       : 75,
+    L       : 76,
+    M       : 77,
+    N       : 78,
+    O       : 79,
+    P       : 80,
+    Q       : 81,
+    R       : 82,
+    S       : 83,
+    T       : 84,
+    U       : 85,
+    V       : 86,
+    W       : 87,
+    X       : 88,
+    Y       : 89,
+    Z       : 90,
+    F1      : 112,
+    F2      : 113,
+    F3      : 114,
+    F4      : 115,
+    F5      : 116,
+    F6      : 117,
+    F7      : 118,
+    F8      : 119,
+    F9      : 120,
+    F10     : 121,
+    F11     : 122,
+    F12     : 123    
+};
\ No newline at end of file
diff --git a/js/level.js b/js/level.js
new file mode 100644 (file)
index 0000000..650636a
--- /dev/null
@@ -0,0 +1,1134 @@
+/*
+* ===========================================================================
+* 
+* Wolf3D Browser Version GPL Source Code
+* Copyright (C) 2012 id Software LLC, a ZeniMax Media company. 
+* 
+* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").  
+* 
+* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+* 
+* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+* 
+* You should have received a copy of the GNU General Public License version 2
+* along with Wolf3D Browser Source Code.  If not, see <http://www.gnu.org/licenses/>.
+* 
+* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
+* 
+* ===========================================================================
+*/
+
+/** 
+ * @namespace 
+ * @description Level management
+ */
+Wolf.Level = (function() {
+
+    Wolf.setConsts({
+        WALL_TILE           : 1,
+        PUSHWALL_TILE       : (1 << 20),
+        DOOR_TILE           : 2,
+        SECRET_TILE         : 4,
+        DRESS_TILE          : 8,
+        BLOCK_TILE          : 16,
+        ACTOR_TILE          : 32,
+        DEADACTOR_TILE      : 64,
+        POWERUP_TILE        : 128,
+        AMBUSH_TILE         : 256,
+        EXIT_TILE           : 512,
+        SECRETLEVEL_TILE    : 1024,
+        ELEVATOR_TILE       : (1 << 11),
+        
+        MAPHEADER_SIZE      : 49,
+        MAP_SIGNATURE       : 0x21444921,
+        
+        TILE_IS_E_TURN      : (1 << 12),
+        TILE_IS_NE_TURN     : (1 << 13),
+        TILE_IS_N_TURN      : (1 << 14),
+        TILE_IS_NW_TURN     : (1 << 15),
+        TILE_IS_W_TURN      : (1 << 16),
+        TILE_IS_SW_TURN     : (1 << 17),
+        TILE_IS_S_TURN      : (1 << 18),
+        TILE_IS_SE_TURN     : (1 << 19),
+        
+        MAX_POWERUPS        : 1000
+    });
+    Wolf.setConsts({
+        SOLID_TILE          : (Wolf.WALL_TILE | Wolf.BLOCK_TILE | Wolf.PUSHWALL_TILE),
+        BLOCKS_MOVE_TILE    : (Wolf.WALL_TILE | Wolf.BLOCK_TILE | Wolf.PUSHWALL_TILE | Wolf.ACTOR_TILE),
+        WAYPOINT_TILE       : (Wolf.TILE_IS_E_TURN | Wolf.TILE_IS_NE_TURN | Wolf.TILE_IS_N_TURN | Wolf.TILE_IS_NW_TURN | 
+                               Wolf.TILE_IS_W_TURN | Wolf.TILE_IS_SW_TURN | Wolf.TILE_IS_S_TURN | Wolf.TILE_IS_SE_TURN )
+    });
+
+    var statinfo = [
+        [false, -1],                    // puddle          spr1v
+        [ true, -1],                    // Green Barrel    "
+        [ true, -1],                    // Table/chairs    "
+        [ true, -1],                    // Floor lamp      "
+        [false, -1],                    // Chandelier      "
+        [ true, -1],                    // Hanged man      "
+        [false, Wolf.pow_alpo],            // Bad food        "
+        [ true, -1],                    // Red pillar      "
+        [ true, -1],                    // Tree            spr2v
+        [false, -1],                    // Skeleton flat   "
+        [ true, -1],                    // Sink            " (SOD:gibs)
+        [ true, -1],                    // Potted plant    "
+        [ true, -1],                    // Urn             "
+        [ true, -1],                    // Bare table      "
+        [false, -1],                    // Ceiling light   "
+        [false, -1],                    // Kitchen stuff   "
+        [ true, -1],                    // suit of armor   spr3v
+        [ true, -1],                    // Hanging cage    "
+        [ true, -1],                    // SkeletoninCage  "
+        [false, -1],                    // Skeleton relax  "
+        [false, Wolf.pow_key1],            // Key 1           "
+        [false, Wolf.pow_key2],            // Key 2           "
+        [ true, -1],                    // stuff                (SOD:gibs)
+        [false, -1],                    // stuff
+        [false, Wolf.pow_food],            // Good food       spr4v
+        [false, Wolf.pow_firstaid],        // First aid       "
+        [false, Wolf.pow_clip],            // Clip            "
+        [false, Wolf.pow_machinegun],   // Machine gun     "
+        [false, Wolf.pow_chaingun],        // Gatling gun     "
+        [false, Wolf.pow_cross],        // Cross           "
+        [false, Wolf.pow_chalice],        // Chalice         "
+        [false, Wolf.pow_bible],        // Bible           "
+        [false, Wolf.pow_crown],        // crown           spr5v
+        [false, Wolf.pow_fullheal],        // one up          "
+        [false, Wolf.pow_gibs],            // gibs            "
+        [ true, -1],                    // barrel          "
+        [ true, -1],                    // well            "
+        [ true, -1],                    // Empty well      "
+        [false, Wolf.pow_gibs],            // Gibs 2          "
+        [ true, -1],                    // flag                "
+        [ true, -1],                    // Call Apogee        spr7v
+        [false, -1],                    // junk            "
+        [false, -1],                    // junk            "
+        [false, -1],                    // junk            "
+        [false, -1],                    // pots            "
+        [ true, -1],                     // stove           " (SOD:gibs)
+        [ true, -1],                    // spears          " (SOD:gibs)
+        [false, -1]                        // vines           "
+    ];
+    
+    for (var i=0;i<statinfo.length;i++) {
+        var info = {
+            idx : i,
+            block : statinfo[i][0],
+            powerup : statinfo[i][1]
+        };
+        statinfo[i] = info;
+    }
+
+    
+
+     /**
+     * @description Create a new level object
+     * @private 
+     * @returns {object} The new level 
+     */
+    function newLevel() {
+        return {
+            // readonly after level load
+            areas : [],
+            tileMap : [],
+            wallTexX : [],
+            wallTexY : [],
+           
+            spawn : {
+                x: -1,
+                y: -1,
+                angle : 0
+            },
+            
+            sprites : [],
+            
+            floorNum : 0,
+            fParTime : 0,
+            sParTime : "",
+            
+            levelName : "", // the descriptive name (Outer Base, etc)
+            mapName : "",   // the server name (base1, etc)
+            nextMap : "",   // go here when fraglimit is hit
+            music : "",
+
+            // state variables
+            state : {
+                framenum : 0,
+                time : 0,
+
+                // intermission state
+                levelCompleted : 0,                // in case the game was saved at the intermission
+                
+                totalSecrets : 0,
+                foundSecrets : 0,
+
+                totalTreasure : 0,
+                foundTreasure : 0,
+
+                totalMonsters : 0,
+                killedMonsters : 0,
+                
+                areaconnect : null,
+                areabyplayer : null,
+                
+                numDoors : 0,
+                doors : [],
+                doorMap : [],
+                
+                powerups : [],
+                numPowerups : 0,
+
+                guards : [],
+                numGuards : 0,
+                
+                startTime : 0,
+                elapsedTime : 0
+            }
+        };
+    }
+    
+    /**
+     * @description Parse map file data.
+     * @private
+     * @param {object} file The file object
+     * @returns {object} The new level object
+     */
+    function parseMapData(file) {
+        var F = Wolf.File,
+            level = newLevel(),
+            length, offset,
+            mapNameLength,
+            musicNameLength,
+            x, y, y0,
+            layer1, layer2, layer3;
+
+        file.position = 0;
+            
+        level.file = file;
+        
+        if (file.size < Wolf.MAPHEADER_SIZE) {
+            throw new Error("Map file size is smaller than mapheader size");
+        }
+        if (F.readUInt32(file) != Wolf.MAP_SIGNATURE) {
+            throw new Error("File signature does not match MAP_SIGNATURE");
+        }
+        
+        rle = F.readUInt16(file);
+        
+        level.width = F.readUInt16(file);
+        level.height = F.readUInt16(file);
+        
+        level.ceiling = [F.readUInt8(file), F.readUInt8(file), F.readUInt8(file), F.readUInt8(file)];
+        level.floor = [F.readUInt8(file), F.readUInt8(file), F.readUInt8(file), F.readUInt8(file)];
+
+        length = [
+            F.readUInt16(file),
+            F.readUInt16(file),
+            F.readUInt16(file)
+        ];
+        offset = [
+            F.readUInt32(file),
+            F.readUInt32(file),
+            F.readUInt32(file)
+        ];
+    
+        mapNameLength = F.readUInt16(file);
+        musicNameLength = F.readUInt16(file);
+        
+        file.position += 4; // (single) fpartime;
+        
+        level.sParTime = F.readString(file, 5);
+
+        if (file.size < (Wolf.MAPHEADER_SIZE + mapNameLength + musicNameLength + length[0] + length[1] + length[2])) {
+            throw new Error("filesize is less than MAPHEADER_SIZE + mapNameLength + musicNameLength + etc");
+        }
+        
+        level.levelName = level.mapName = F.readString(file, mapNameLength);
+        level.music = F.readString(file, musicNameLength);
+
+        level.plane1 = readPlaneData(file, offset[0], length[0], rle);
+        level.plane2 = readPlaneData(file, offset[1], length[1], rle);
+        level.plane3 = readPlaneData(file, offset[2], length[2], rle);
+        
+        
+        
+        // jseidelin: hack disabled since we only use up to map 30
+        // HUGE HACK to take out the pushwall maze that occasionally
+        // gets players stuck in level E4M2 without actually touching
+        // a map editor...
+        /*
+        if (file == "maps/w31.map") {
+            for (x = 22; x <= 32; x++) {
+                for (y0 = 30; y0 <= 32; y0++) {
+                    Plane1[y0 * 64 + x] = Plane1[30*64+21];
+                    Plane2[y0 * 64 + x] = Plane2[30*64+21];
+                    Plane3[y0 * 64 + x] = Plane3[30*64+21];
+                }
+            }
+        }
+        */
+        
+        Wolf.Doors.reset(level);
+        
+        for (x=0;x<64;x++) {
+            level.areas[x] = [];
+            level.tileMap[x] = [];
+            level.wallTexX[x] = [];
+            level.wallTexY[x] = [];
+            
+            for (y=0;y<64;y++) {
+                level.areas[x][y] = 0;
+                level.tileMap[x][y] = 0;
+                level.wallTexX[x][y] = 0;
+                level.wallTexY[x][y] = 0;
+            }
+        }
+    
+   
+        for (y0 = 0; y0 < 64; ++y0) {
+            for (x = 0; x < 64; ++x) {
+                y = 63 - y0; 
+                
+                layer1 = level.plane1[y0 * 64 + x];
+                layer2 = level.plane2[y0 * 64 + x];
+                layer3 = level.plane3[y0 * 64 + x];
+
+                // if server, process obj layer!
+                if (layer2) {
+                    spawnObj(level, layer2, x, y);
+                }
+
+                // Map data layer
+                if (layer1 == 0) {
+                    level.areas[x][y] = -3; // unknown area
+                } else if (layer1 < 0x6a) { // solid map object
+                    if ((layer1 >= 0x5A && layer1 <= 0x5F) || layer1 == 0x64 || layer1 == 0x65) { // door
+                        level.tileMap[x][y] |= Wolf.DOOR_TILE;
+                        Wolf.Doors.spawn(level, x, y, layer1);
+                        level.areas[x][y] = -2; // door area
+                    } else {
+                        level.tileMap[x][y] |= Wolf.WALL_TILE;
+                        level.wallTexX[x][y] = (layer1-1) * 2 + 1;
+                        level.wallTexY[x][y] = (layer1-1) * 2;
+                        level.areas[x][y] = -1; // wall area
+                        if (layer1 == 0x15) { // elevator
+                            level.tileMap[x][y] |= Wolf.ELEVATOR_TILE;
+                        }
+                    }
+                } else if (layer1 == 0x6a) { // Ambush floor tile
+                    level.tileMap[x][y] |= Wolf.AMBUSH_TILE;
+                    level.areas[x][y] = -3; // unknown area
+                } else if (layer1 >= Wolf.FIRSTAREA && layer1 < (Wolf.FIRSTAREA + Wolf.NUMAREAS)) { // area
+                    if (layer1 == Wolf.FIRSTAREA) { // secret level
+                        level.tileMap[x][y] |= Wolf.SECRETLEVEL_TILE;
+                    }
+                    level.areas[x][y] = layer1 - Wolf.FIRSTAREA;// spawn area
+                } else {
+                    level.areas[x][y] = -3; // unknown area
+                }
+                // End of the map data layer
+            }
+        }
+        
+        
+        // JDC: try to replace all the unknown areas with an adjacent area, to
+        // avoid the silent attack / no damage problem when you get an ambush
+        // guard stuck on their original tile
+        for (x=1;x<63;x++) {
+            for (y=1;y<63;y++) {
+                if (level.areas[x][y] != -3) {
+                    continue;
+                }
+                if (level.areas[x-1][y] >= 0) {
+                    level.areas[x][y] = level.areas[x-1][y];
+                } else if (level.areas[x+1][y] >= 0) {
+                    level.areas[x][y] = level.areas[x+1][y];
+                } else if (level.areas[x][y-1] >= 0) {
+                    level.areas[x][y] = level.areas[x][y-1];
+                } else if (level.areas[x+1][y+1] >= 0) {
+                    level.areas[x][y] = level.areas[x][y+1];
+                }
+            }
+        }
+
+        Wolf.Doors.setAreas(level);
+    
+        return level;
+    }
+   
+    /**
+     * @description Read plane data from map data
+     * @private
+     * @param {object} file The file object
+     * @param {number} offset The starting position
+     * @param {number} length The length of the plane data
+     * @param {number} rle The RLE tag
+     * @returns {array} The plane data
+     */
+    function readPlaneData(file, offset, length, rle) {
+        file.position = offset;
+        
+        var expandedLength = Wolf.File.readUInt16(file),
+            carmackData = Wolf.File.readBytes(file, length - 2),
+            expandedData = carmackExpand(carmackData, expandedLength);
+        
+        return rlewExpand(expandedData.slice(1), 64*64*2, rle);
+    }
+
+    
+    /**
+     * @description Expand RLE data
+     * @private
+     * @param {array} source The source data
+     * @param {number} length The length of the expanded data
+     * @param {number} rlewtag The RLE tag
+     * @returns {array} The expanded data
+     */
+    function rlewExpand(source, length, rlewtag) {
+        var value,
+            count,
+            i,
+            end, /* W16 */
+            inptr = 0,
+            outptr = 0,
+            dest = [];
+    
+        end = outptr + (length >> 1);
+        
+        do {
+            value = source[inptr++];
+            if (value != rlewtag) {
+                // uncompressed
+                dest[outptr++] = value;
+            } else {
+                // compressed string
+                count = source[inptr++];
+                value = source[inptr++];
+                for (i=1;i<=count;++i) {
+                    dest[outptr++] = value;
+                }
+            }
+        } while (outptr < end);
+        
+        return dest;
+    }
+    
+    /**
+     * @description Expand Carmackized data
+     * @private
+     * @param {array} source The source data
+     * @param {number} length The length of the expanded data
+     * @returns {array} The expanded data
+     */
+    function carmackExpand(source, length) {
+        var NEARTAG = 0xA7,
+            FARTAG  = 0xA8;
+
+        var chhigh, offset, /* W32 */
+            copyptr, outptr, /* W16 */
+            inptr, /* W8 */
+            ch, count, /* W16 */
+            dest;
+
+        length /= 2;
+
+        inptr = 0;
+        outptr = 0;
+        dest = [];
+        
+        function W16(b, i) {
+            return b[i] + (b[i+1] << 8);
+        }
+
+        while (length) {
+            ch = source[inptr] + (source[inptr+1] << 8);
+            //ch = W16(source, inptr);
+            inptr += 2;
+            chhigh = ch >> 8;
+            if (chhigh == NEARTAG) {
+                count = ch & 0xff;
+                if (!count) { 
+                    // have to insert a word containing the tag byte
+                    ch |= source[inptr++];
+                    dest[outptr++] = ch;
+                    length--;
+                } else {
+                    offset = source[inptr++];
+                    copyptr = outptr - offset;
+                    length -= count;
+                    while (count--) {
+                        dest[outptr++] = dest[copyptr++];
+                    }
+                }
+            } else if (chhigh == FARTAG) {
+                count = ch & 0xff;
+                if (!count)    {
+                    // have to insert a word containing the tag byte
+                    ch |= source[inptr++];
+                    dest[outptr++] = ch;
+                    length--;
+                } else {
+                    offset = source[inptr] + (source[inptr+1] << 8);
+                    //offset = W16(source, inptr);
+                    inptr += 2;
+                    copyptr = offset;
+                    length -= count;
+                    while (count--) {
+                        dest[outptr++] = dest[copyptr++];
+                    }
+                }
+            } else {
+                dest[outptr++] = ch;
+                length--;
+            }
+        }
+        return dest;
+    }
+    
+    /**
+     * @description Load level data
+     * @memberOf Wolf.Level
+     * @param {string} filename The name of the level file.
+     * @param {function} callback Called with the resulting level object.
+     * @returns {object} The level object.
+     */
+    function load(filename, callback) {
+        Wolf.File.open(filename, Wolf.MapData, function(error, file) {
+            var level;
+            if (error) {
+                callback(error);
+            }
+            try {
+                level = parseMapData(file);
+            } catch(error) {
+                callback(error);
+                return;
+            }
+            callback(null, level);
+        });
+    }
+    
+    function reload(level) {
+        return parseMapData(level.file);
+    }
+
+    
+    /**
+     * @description Spawn an object in the level at the specified position.
+     * @private
+     * @param {object} level The level object.
+     * @param {number} type The object type.
+     * @param {number} x The x coordinate.
+     * @param {number} y The y coordinate.
+     */
+    function spawnObj(level, type, x, y) {
+        if (type >= 23 && type < 23 + statinfo.length) { // static object
+            spawnStatic(level, type - 23, x, y);
+            return;
+        }
+
+        switch (type) {
+            case 0x13: // start N
+                level.spawn.x = Wolf.TILE2POS(x);
+                level.spawn.y = Wolf.TILE2POS(y);
+                level.spawn.angle = Wolf.ANG_90;
+                break;
+            case 0x14: // start E
+                level.spawn.x = Wolf.TILE2POS(x);
+                level.spawn.y = Wolf.TILE2POS(y);
+                level.spawn.angle = Wolf.ANG_0;
+                break;
+            case 0x15: // start S
+                level.spawn.x = Wolf.TILE2POS(x);
+                level.spawn.y = Wolf.TILE2POS(y);
+                level.spawn.angle = Wolf.ANG_270;
+                break;
+            case 0x16: // start W
+                level.spawn.x = Wolf.TILE2POS(x);
+                level.spawn.y = Wolf.TILE2POS(y);
+                level.spawn.angle = Wolf.ANG_180;
+                break;
+            case 0x5a: // turn E
+                level.tileMap[x][y] |= Wolf.TILE_IS_E_TURN;//FIXME!
+                break;
+            case 0x5b: // turn NE
+                level.tileMap[x][y] |= Wolf.TILE_IS_NE_TURN;//FIXME!
+                break;
+            case 0x5c: // turn N
+                level.tileMap[x][y] |= Wolf.TILE_IS_N_TURN;//FIXME!
+                break;
+            case 0x5d: // turn NW
+                level.tileMap[x][y] |= Wolf.TILE_IS_NW_TURN;//FIXME!
+                break;
+            case 0x5e: // turn W
+                level.tileMap[x][y] |= Wolf.TILE_IS_W_TURN;//FIXME!
+                break;
+            case 0x5f: // turn SW
+                level.tileMap[x][y] |= Wolf.TILE_IS_SW_TURN;//FIXME!
+                break;
+            case 0x60: // turn S
+                level.tileMap[x][y] |= Wolf.TILE_IS_S_TURN;//FIXME!
+                break;
+            case 0x61: // turn SE
+                level.tileMap[x][y] |= Wolf.TILE_IS_SE_TURN;//FIXME!
+                break;
+            case 0x62: // pushwall modifier
+                level.tileMap[x][y] |= Wolf.SECRET_TILE;
+                level.state.totalSecrets++;
+                break;
+            case 0x63: // Victory trigger
+                level.tileMap[x][y] |= Wolf.EXIT_TILE;
+                break;
+            // spawn guards
+        } // end of switch( type )
+
+    }
+    
+    /**
+     * @description Spawn a static object at the specified position.
+     * @private
+     * @param {object} level The level object.
+     * @param {number} type The static object type.
+     * @param {number} x The x coordinate.
+     * @param {number} y The y coordinate.
+     */
+    function spawnStatic(level, type, x, y) {
+        var sprite, pu;
+
+        if (statinfo[type].powerup == -1 ) {
+            if (statinfo[type].block) {    // blocking static
+                level.tileMap[x][y] |= Wolf.BLOCK_TILE;
+            } else {                    // dressing static
+                level.tileMap[x][y] |= Wolf.DRESS_TILE;
+            }
+
+            sprite = Wolf.Sprites.getNewSprite(level);
+            if (!sprite) {
+                return;
+            }
+
+            Wolf.Sprites.setPos(level, sprite, Wolf.TILE2POS(x), Wolf.TILE2POS(y), 0 );
+            Wolf.Sprites.setTex(level, sprite, 0, Wolf.SPR_STAT_0 + type);
+        } else {
+            pu = statinfo[type].powerup;
+            Wolf.Powerups.spawn(level, x, y, pu);
+            
+            if (pu == Wolf.pow_cross || pu == Wolf.pow_chalice || pu == Wolf.pow_bible || pu == Wolf.pow_crown || pu == Wolf.pow_fullheal) {
+                level.state.totalTreasure++; // FIXME: move this to Powerup_Spawn Function!
+            }
+        }
+    }
+
+
+
+    var cachedGuard = 0,
+        cachedOfficer = 0,
+        cachedSS = 0,
+        cachedDog = 0,
+        cachedMutant = 0,
+        progress_bar = 0;
+
+
+    /**
+     * @description Spawn all actors and mark down special places.
+     * @memberOf Wolf.Level
+     * @param {object} level The level object.
+     * @param {number} skill The difficulty level.
+     */
+    function scanInfoPlane(level, skill) {
+        var x, y, tile;
+        
+        cachedGuard = 0;
+        cachedOfficer = 0;
+        cachedSS = 0;
+        cachedDog = 0;
+        cachedMutant = 0;
+        progress_bar = 0;
+
+        for (y = 0;y < 64;++y) {
+            for (x = 0;x < 64;++x) {
+                tile = level.plane2[(63 - y) * 64 + x];
+                if (!tile) {
+                    continue;
+                }
+
+                switch (tile) {
+                    // guard
+                    case 180:
+                    case 181:
+                    case 182:
+                    case 183:
+                        if (skill < Wolf.gd_hard) {
+                            break;
+                        }
+&n