]> git.jsancho.org Git - lugaru.git/blobdiff - Source/Objects/Person.cpp
Added .at usage to have clearer crash when dialogue contains invalid participant
[lugaru.git] / Source / Objects / Person.cpp
index c1b35e443d365d693e56f1cef87daf03d1ee3891..874194b4a53419537c177aaf15352f9bf65002dd 100644 (file)
@@ -27,6 +27,7 @@ along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
 #include "Level/Awards.hpp"
 #include "Level/Dialog.hpp"
 #include "Utils/Folders.hpp"
+#include "Tutorial.hpp"
 
 extern float multiplier;
 extern Terrain terrain;
@@ -50,15 +51,13 @@ extern float woozy;
 extern float viewdistance;
 extern float blackout;
 extern int difficulty;
-extern bool decals;
+extern bool decalstoggle;
 extern float fadestart;
 extern bool freeze;
 extern bool winfreeze;
 extern bool showpoints;
 extern bool immediate;
-extern int tutoriallevel;
 extern float smoketex;
-extern int tutorialstage;
 extern bool reversaltrain;
 extern bool canattack;
 extern bool cananger;
@@ -68,6 +67,13 @@ extern float hostiletime;
 
 extern bool gamestarted;
 
+extern XYZ envsound[30];
+extern float envsoundvol[30];
+extern int numenvsounds;
+extern float envsoundlife[30];
+
+extern XYZ windvector;
+
 std::vector<std::shared_ptr<Person>> Person::players(1, std::shared_ptr<Person>(new Person()));
 
 Person::Person() :
@@ -479,7 +485,7 @@ void Person::CheckKick()
 
         victim->spurt = 1;
         DoBlood(.2, 250);
-        if (tutoriallevel != 1)
+        if (!Tutorial::active)
             emit_sound_at(heavyimpactsound, victim->coords);
         victim->RagDoll(0);
         for (unsigned i = 0; i < victim->skeleton.joints.size(); i++) {
@@ -559,8 +565,8 @@ int Person::getIdle()
 {
     if (Dialog::inDialog() && (howactive == typeactive) && (creature == rabbittype))
         return talkidleanim;
-    if (hasvictim && (victim != this->shared_from_this())/*||(id==0&&attackkeydown)*/)
-        if (/*(id==0&&attackkeydown)||*/(!victim->dead && victim->aitype != passivetype &&
+    if (hasvictim && (victim != this->shared_from_this())) {
+        if ((!victim->dead && victim->aitype != passivetype &&
             victim->aitype != searchtype && aitype != passivetype && aitype != searchtype &&
             victim->id < Person::players.size())) {
             if ((aitype == playercontrolled && stunned <= 0 && weaponactive == -1) || pause) {
@@ -582,6 +588,7 @@ int Person::getIdle()
             if (aitype != playercontrolled && stunned <= 0 && creature != wolftype && !pause)
                 return fightsidestep;
         }
+    }
     if ((damage > permanentdamage || damage > damagetolerance * .8 || deathbleeding > 0) && creature != wolftype)
         return hurtidleanim;
     if (howactive == typesitting) return sitanim;
@@ -680,7 +687,7 @@ void Person::DoBlood(float howmuch, int which)
     // FIXME: should abstract out inputs
     static int bleedxint, bleedyint;
     static XYZ bloodvel;
-    if (bloodtoggle && tutoriallevel != 1) {
+    if (bloodtoggle && !Tutorial::active) {
         if (bleeding <= 0 && spurt) {
             spurt = 0;
             for (int i = 0; i < 3; i++) {
@@ -722,7 +729,7 @@ void Person::DoBlood(float howmuch, int which)
                     }
                 }
         }
-        if (decals) {
+        if (decalstoggle) {
             // FIXME: manipulating attributes
             bleeding = howmuch + (float)abs(Random() % 100) / 200 - .25;
             bleedxint = 0;
@@ -760,7 +767,7 @@ void Person::DoBloodBig(float howmuch, int which)
     if (howmuch && id == 0)
         blooddimamount = 1;
 
-    if (tutoriallevel != 1 || id == 0)
+    if (!Tutorial::active || id == 0)
         if (aitype != playercontrolled && howmuch > 0) {
             // play pain sounds
             int whichsound = -1;
@@ -790,7 +797,7 @@ void Person::DoBloodBig(float howmuch, int which)
         Game::flash(.5, 0);
     }
 
-    if (bloodtoggle && decals && tutoriallevel != 1) {
+    if (bloodtoggle && decalstoggle && !Tutorial::active) {
         if (bleeding <= 0 && spurt) {
             spurt = 0;
             for (int i = 0; i < 3; i++) {
@@ -945,7 +952,7 @@ void Person::DoBloodBig(float howmuch, int which)
     deathbleeding += bleeding;
     bloodloss += bleeding * 3;
 
-    if (tutoriallevel != 1 && aitype != playercontrolled && bloodloss > damagetolerance * 2 / 3 && bloodloss < damagetolerance && creature == rabbittype) {
+    if (!Tutorial::active && aitype != playercontrolled && bloodloss > damagetolerance * 2 / 3 && bloodloss < damagetolerance && creature == rabbittype) {
         if (abs(Random() % 2) == 0) {
             aitype = gethelptype;
             lastseentime = 12;
@@ -973,7 +980,7 @@ bool Person::DoBloodBigWhere(float howmuch, int which, XYZ where)
     float coordsx, coordsy;
     float total;
 
-    if (bloodtoggle && decals && tutoriallevel != 1) {
+    if (bloodtoggle && decalstoggle && !Tutorial::active) {
         where -= coords;
         if (!skeleton.free)
             where = DoRotation(where, 0, -yaw, 0);
@@ -1158,7 +1165,7 @@ bool Person::DoBloodBigWhere(float howmuch, int which, XYZ where)
     deathbleeding += bleeding;
     bloodloss += bleeding * 3;
 
-    if (tutoriallevel != 1 && aitype != playercontrolled && bloodloss > damagetolerance * 2 / 3 && bloodloss < damagetolerance && creature == rabbittype) {
+    if (!Tutorial::active && aitype != playercontrolled && bloodloss > damagetolerance * 2 / 3 && bloodloss < damagetolerance && creature == rabbittype) {
         if (abs(Random() % 2) == 0) {
             aitype = gethelptype;
             lastseentime = 12;
@@ -1183,7 +1190,7 @@ void Person::Reverse()
             || staggerdelay <= 0)
             && victim->animTarget != jumpupanim
             && victim->animTarget != jumpdownanim
-            && (tutoriallevel != 1 || cananger)
+            && (!Tutorial::active || cananger)
             && hostile))
         return;
 
@@ -1486,40 +1493,44 @@ void Person::Reverse()
  */
 void Person::DoDamage(float howmuch)
 {
-    // subtract health (temporary?)
-    if (tutoriallevel != 1)
-        damage += howmuch / power;
     // stats?
-    if (id != 0)
-        damagedealt += howmuch / power;
-    if (id == 0)
+    if (id == 0) {
         damagetaken += howmuch / power;
+    } else {
+        damagedealt += howmuch / power;
+    }
 
     // reset bonuses
-    if (id == 0 && (bonus == solidhit || bonus == twoxcombo || bonus == threexcombo || bonus == fourxcombo || bonus == megacombo))
+    if (id == 0 && (bonus == solidhit || bonus == twoxcombo || bonus == threexcombo || bonus == fourxcombo || bonus == megacombo)) {
         bonus = 0;
+    }
+
     // subtract health
-    if (tutoriallevel != 1)
+    if (!Tutorial::active) {
+        damage += howmuch / power;
         permanentdamage += howmuch / 2 / power;
-    if (tutoriallevel != 1)
         superpermanentdamage += howmuch / 4 / power;
+    }
     // visual effects
     if (permanentdamage > damagetolerance / 2 && permanentdamage - howmuch < damagetolerance / 2 && Random() % 2)
         DoBlood(1, 255);
     if ((permanentdamage > damagetolerance * .8 && Random() % 2 && !deathbleeding) || spurt)
         DoBlood(1, 255);
     spurt = 0;
-    if (id == 0)
+    if (id == 0) {
         camerashake += howmuch / 100;
-    if (id == 0 && ((howmuch > 50 && damage > damagetolerance / 2)))
-        blackout = damage / damagetolerance;
-    if (blackout > 1)
-        blackout = 1;
+        if ((howmuch > 50 && damage > damagetolerance / 2)) {
+            blackout = damage / damagetolerance;
+            if (blackout > 1) {
+                blackout = 1;
+            }
+        }
+    }
 
     // cancel attack?
-    if (aitype == passivetype && damage < damagetolerance && ((tutoriallevel != 1 || cananger) && hostile))
+    if (aitype == passivetype && damage < damagetolerance && ((!Tutorial::active || cananger) && hostile))
         aitype = attacktypecutoff;
-    if (tutoriallevel != 1 && aitype != playercontrolled && damage < damagetolerance && damage > damagetolerance * 2 / 3 && creature == rabbittype) {
+    if (!Tutorial::active && aitype != playercontrolled && damage < damagetolerance && damage > damagetolerance * 2 / 3 && creature == rabbittype) {
         if (abs(Random() % 2) == 0) {
             aitype = gethelptype;
             lastseentime = 12;
@@ -1560,7 +1571,7 @@ void Person::DoDamage(float howmuch)
     }
 
     // play sounds
-    if (tutoriallevel != 1 || id == 0)
+    if (!Tutorial::active || id == 0) {
         if (speechdelay <= 0 && !dead && aitype != playercontrolled) {
             int whichsound = -1;
 
@@ -1584,6 +1595,7 @@ void Person::DoDamage(float howmuch)
                 addEnvSound(coords);
             }
         }
+    }
     speechdelay = .3;
 }
 
@@ -1901,7 +1913,7 @@ void Person::Puff(int whichlabel)
 /* EFFECT
  * I think I added this in an attempt to clean up code
  */
-void Person::setAnimation(int animation)
+void Person::setTargetAnimation(int animation)
 {
     animTarget = animation;
     frameTarget = 0;
@@ -1934,7 +1946,7 @@ void Person::DoAnimations()
             vel[2] = velocity.z;
 
             if (id == 0) {
-                OPENAL_3D_SetAttributes(channels[whooshsound], gLoc, vel);
+                OPENAL_3D_SetAttributes(channels[whooshsound], gLoc);
                 OPENAL_SetVolume(channels[whooshsound], 64 * findLength(&velocity) / 5);
             }
             if (((velocity.y < -15) || (crouchkeydown && velocity.y < -8)) && abs(velocity.y) * 4 > fast_sqrt(velocity.x * velocity.x * velocity.z * velocity.z))
@@ -2031,7 +2043,7 @@ void Person::DoAnimations()
                 drawtogglekeydown = 1;
             }
             //Footstep sounds
-            if (tutoriallevel != 1 || id == 0)
+            if (!Tutorial::active || id == 0)
                 if ((targetFrame().label && (targetFrame().label < 5 || targetFrame().label == 8))) {
                     int whichsound = -1;
                     if (onterrain) {
@@ -2079,7 +2091,7 @@ void Person::DoAnimations()
                     } else if (targetFrame().label == 4) {
                         whichsound = knifeswishsound;
                     }
-                    if (targetFrame().label == 8 && tutoriallevel != 1) {
+                    if (targetFrame().label == 8 && !Tutorial::active) {
                         whichsound = landsound2;
                     }
 
@@ -2104,7 +2116,7 @@ void Person::DoAnimations()
                 }
 
             //Combat sounds
-            if (tutoriallevel != 1 || id == 0)
+            if (!Tutorial::active || id == 0)
                 if (speechdelay <= 0)
                     if (animTarget != crouchstabanim && animTarget != swordgroundstabanim && animTarget != staffgroundsmashanim)
                         if ((targetFrame().label && (targetFrame().label < 5 || targetFrame().label == 8))) {
@@ -2336,7 +2348,7 @@ void Person::DoAnimations()
             }
             if ((Animation::animations[animTarget].attack == normalattack || animTarget == walljumprightkickanim || animTarget == walljumpleftkickanim) && (!feint) && (victim->skeleton.free != 2 || animTarget == killanim || animTarget == dropkickanim || animTarget == crouchstabanim || animTarget == swordgroundstabanim || animTarget == staffgroundsmashanim)) {
                 if (animTarget == spinkickanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) {
-                    if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && 3 && Animation::animations[victim->animTarget].height != lowheight) {
+                    if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && Animation::animations[victim->animTarget].height != lowheight) {
                         escapednum = 0;
                         if (id == 0)
                             camerashake += .4;
@@ -2346,7 +2358,7 @@ void Person::DoAnimations()
                             if (creature == wolftype)
                                 DoBloodBig(0, 250);
                         }
-                        if (tutoriallevel != 1) {
+                        if (!Tutorial::active) {
                             emit_sound_at(heavyimpactsound, victim->coords, 128.);
                         }
                         if (creature == wolftype) {
@@ -2372,7 +2384,7 @@ void Person::DoAnimations()
                 }
 
                 if (animTarget == wolfslapanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) {
-                    if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && 3 && Animation::animations[victim->animTarget].height != lowheight) {
+                    if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && Animation::animations[victim->animTarget].height != lowheight) {
                         escapednum = 0;
                         if (id == 0)
                             camerashake += .4;
@@ -2411,7 +2423,7 @@ void Person::DoAnimations()
                             camerashake += .4;
                         victim->spurt = 1;
                         DoBlood(.2, 250);
-                        if (tutoriallevel != 1) {
+                        if (!Tutorial::active) {
                             emit_sound_at(heavyimpactsound, victim->coords, 160.);
                         }
                         if (creature == wolftype) {
@@ -2446,7 +2458,7 @@ void Person::DoAnimations()
                             camerashake += .4;
                         victim->spurt = 1;
                         DoBlood(.2, 250);
-                        if (tutoriallevel != 1) {
+                        if (!Tutorial::active) {
                             emit_sound_at(heavyimpactsound, victim->coords, 160.);
                         }
                         if (creature == wolftype) {
@@ -2537,7 +2549,7 @@ void Person::DoAnimations()
                         escapednum = 0;
                         if (id == 0)
                             camerashake += .4;
-                        if (tutoriallevel != 1) {
+                        if (!Tutorial::active) {
                             emit_sound_at(heavyimpactsound, coords, 128.);
                         }
                         XYZ relative;
@@ -2562,7 +2574,7 @@ void Person::DoAnimations()
                         escapednum = 0;
                         if (id == 0)
                             camerashake += .4;
-                        if (tutoriallevel != 1) {
+                        if (!Tutorial::active) {
                             emit_sound_at(thudsound, coords);
                         }
 
@@ -2795,7 +2807,7 @@ void Person::DoAnimations()
                             victim->spurt = 1;
                             DoBlood(.2, 235);
                         }
-                        if (tutoriallevel != 1) {
+                        if (!Tutorial::active) {
                             emit_sound_at(heavyimpactsound, victim->coords, 128);
                         }
 
@@ -2830,15 +2842,15 @@ void Person::DoAnimations()
                         if (id == 0)
                             camerashake += .4;
                         if (victim->damage <= victim->damagetolerance - 60 && normaldotproduct(victim->facing, victim->coords - coords) < (scale * 5) * (scale * 5) * 0 && Animation::animations[victim->animTarget].height != lowheight) {
-                            if (tutoriallevel != 1) {
+                            if (!Tutorial::active) {
                                 emit_sound_at(thudsound, victim->coords);
                             }
                         } else if (victim->damage <= victim->damagetolerance - 60 && normaldotproduct(victim->facing, victim->coords - coords) < (scale * 5) * (scale * 5) * 0 && Animation::animations[victim->animTarget].height == lowheight) {
-                            if (tutoriallevel != 1) {
+                            if (!Tutorial::active) {
                                 emit_sound_at(whooshhitsound, victim->coords);
                             }
                         } else {
-                            if (tutoriallevel != 1) {
+                            if (!Tutorial::active) {
                                 emit_sound_at(heavyimpactsound, victim->coords);
                             }
                         }
@@ -2920,11 +2932,12 @@ void Person::DoAnimations()
                     if (hasvictim)
                         if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 4.5 &&/*Animation::animations[victim->animTarget].height!=lowheight&&*/victim->animTarget != dodgebackanim && victim->animTarget != rollanim) {
                             escapednum = 0;
-                            if (tutoriallevel != 1)
+                            if (!Tutorial::active) {
                                 victim->DoBloodBig(1.5 / victim->armorhigh, 225);
+                            }
 
                             award_bonus(id, Slicebonus);
-                            if (tutoriallevel != 1) {
+                            if (!Tutorial::active) {
                                 emit_sound_at(knifeslicesound, victim->coords);
                             }
                             //victim->jointVel(abdomen)+=relative*damagemult*200;
@@ -2941,11 +2954,12 @@ void Person::DoAnimations()
                             if (aitype != playercontrolled)
                                 weaponmissdelay = .6;
 
-                            if (tutoriallevel != 1)
-                                if (bloodtoggle && !weapons[weaponids[weaponactive]].bloody)
+                            if (!Tutorial::active) {
+                                if (bloodtoggle && !weapons[weaponids[weaponactive]].bloody) {
                                     weapons[weaponids[weaponactive]].bloody = 1;
-                            if (tutoriallevel != 1)
+                                }
                                 weapons[weaponids[weaponactive]].blooddrip += 3;
+                            }
 
                             XYZ footvel, footpoint;
                             footvel = 0;
@@ -2954,19 +2968,18 @@ void Person::DoAnimations()
                             } else {
                                 footpoint = DoRotation((victim->jointPos(abdomen) + victim->jointPos(neck)) / 2, 0, victim->yaw, 0) * victim->scale + victim->coords;
                             }
-                            if (tutoriallevel != 1) {
-                                if (bloodtoggle)
+                            if (Tutorial::active) {
+                                Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 1, 1, .6, .3);
+                            } else {
+                                if (bloodtoggle) {
                                     Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .6, .3);
+                                }
                                 footvel = DoRotation(facing, 0, 90, 0) * .8;
-                                //footvel.y-=.3;
                                 Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 7, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9);
                                 Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9);
                                 Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 5, 1, 1, 1, .2, 1);
                                 Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 2, 1, 1, 1, .2, 1);
                             }
-                            if (tutoriallevel == 1) {
-                                Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 1, 1, .6, .3);
-                            }
                             victim->DoDamage(damagemult * 0);
                         }
                 }
@@ -2975,31 +2988,25 @@ void Person::DoAnimations()
                         if (victim->weaponactive == -1 || normaldotproduct(victim->facing, victim->coords - coords) > 0 || (Random() % 2 == 0)) {
                             award_bonus(id, Slashbonus);
                             escapednum = 0;
-                            if (tutoriallevel != 1) {
+                            if (!Tutorial::active) {
                                 if (normaldotproduct(victim->facing, victim->coords - coords) < 0)
                                     victim->DoBloodBig(2 / victim->armorhigh, 190);
                                 else
                                     victim->DoBloodBig(2 / victim->armorhigh, 185);
                                 victim->deathbleeding = 1;
                                 emit_sound_at(swordslicesound, victim->coords);
-                            }
-                            //victim->jointVel(abdomen)+=relative*damagemult*200;
-                            if (tutoriallevel != 1) {
                                 victim->frameTarget = 0;
                                 victim->animTarget = staggerbackhardanim;
                                 victim->targetyaw = targetyaw + 180;
                                 victim->target = 0;
-                            }
-
-                            if (tutoriallevel != 1) {
-                                if (bloodtoggle && !weapons[weaponids[weaponactive]].bloody)
+                                if (bloodtoggle && !weapons[weaponids[weaponactive]].bloody) {
                                     weapons[weaponids[weaponactive]].bloody = 1;
+                                }
                                 weapons[weaponids[weaponactive]].blooddrip += 3;
 
                                 float bloodlossamount;
                                 bloodlossamount = 200 + abs((float)(Random() % 40)) - 20;
                                 victim->bloodloss += bloodlossamount / victim->armorhigh;
-                                //victim->bloodloss+=100*(6.5-distsq(&coords,&victim->coords));
                                 victim->DoDamage(damagemult * 0);
 
                                 XYZ footvel, footpoint;
@@ -3009,8 +3016,9 @@ void Person::DoAnimations()
                                 } else {
                                     footpoint = DoRotation((victim->jointPos(abdomen) + victim->jointPos(neck)) / 2, 0, victim->yaw, 0) * victim->scale + victim->coords;
                                 }
-                                if (bloodtoggle)
+                                if (bloodtoggle) {
                                     Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .9, .3);
+                                }
                                 footvel = DoRotation(facing, 0, 90, 0) * .8;
                                 footvel.y -= .3;
                                 Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 7, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9);
@@ -3060,7 +3068,7 @@ void Person::DoAnimations()
 
                 if (animTarget == staffhitanim && Animation::animations[animTarget].frames[frameCurrent].label == 5 && victim->animTarget != rollanim) {
                     if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 6.5 && victim->animTarget != dodgebackanim && victim->animTarget != sweepanim) {
-                        if (tutoriallevel != 1) {
+                        if (!Tutorial::active) {
                             weapons[weaponids[0]].damage += .4 + float(abs(Random() % 100) - 50) / 250;
                             escapednum = 0;
                             if (id == 0)
@@ -3084,7 +3092,7 @@ void Person::DoAnimations()
                         victim->jointVel(head) += relative * damagemult * 230;
                         victim->jointVel(neck) += relative * damagemult * 230;
                         victim->Puff(head);
-                        if (tutoriallevel != 1) {
+                        if (!Tutorial::active) {
                             victim->DoDamage(damagemult * 120 / victim->protectionhigh);
 
                             award_bonus(id, solidhit, 30);
@@ -3094,7 +3102,7 @@ void Person::DoAnimations()
 
                 if (animTarget == staffspinhitanim && Animation::animations[animTarget].frames[frameCurrent].label == 5 && victim->animTarget != rollanim) {
                     if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 6.5 && victim->animTarget != dodgebackanim && victim->animTarget != sweepanim) {
-                        if (tutoriallevel != 1) {
+                        if (!Tutorial::active) {
                             weapons[weaponids[0]].damage += .6 + float(abs(Random() % 100) - 50) / 250;
                             escapednum = 0;
                             if (id == 0)
@@ -3116,7 +3124,7 @@ void Person::DoAnimations()
                         victim->jointVel(head) += relative * damagemult * 220;
                         victim->jointVel(neck) += relative * damagemult * 220;
                         victim->Puff(head);
-                        if (tutoriallevel != 1) {
+                        if (!Tutorial::active) {
                             victim->DoDamage(damagemult * 350 / victim->protectionhead);
 
                             award_bonus(id, solidhit, 60);
@@ -3127,7 +3135,7 @@ void Person::DoAnimations()
                 if (animTarget == staffgroundsmashanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) {
                     if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 6.5) {
                         escapednum = 0;
-                        if (tutoriallevel != 1) {
+                        if (!Tutorial::active) {
                             if (!victim->dead)
                                 weapons[weaponids[0]].damage += .4 + float(abs(Random() % 100) - 50) / 500;
                             if (id == 0)
@@ -3164,7 +3172,7 @@ void Person::DoAnimations()
                             }
                         }
                         victim->Puff(abdomen);
-                        if (tutoriallevel != 1) {
+                        if (!Tutorial::active) {
                             victim->DoDamage(damagemult * 100 / victim->protectionhigh);
 
                             if (!victim->dead) {
@@ -3196,7 +3204,7 @@ void Person::DoAnimations()
                                 victim->skeleton.joints[i].velocity += relative * damagemult * 40;
                             }
                             victim->jointVel(head) += relative * damagemult * 200;
-                            if (tutoriallevel != 1) {
+                            if (!Tutorial::active) {
                                 emit_sound_at(heavyimpactsound, victim->coords, 128.);
                             }
                             victim->Puff(head);
@@ -3219,7 +3227,7 @@ void Person::DoAnimations()
                             victim->animTarget = staggerbackhighanim;
                             victim->targetyaw = targetyaw + 180;
                             victim->target = 0;
-                            if (tutoriallevel != 1) {
+                            if (!Tutorial::active) {
                                 emit_sound_at(landsound2, victim->coords, 128.);
                             }
                             victim->Puff(abdomen);
@@ -3241,7 +3249,7 @@ void Person::DoAnimations()
                         escapednum = 0;
                         if (id == 0)
                             camerashake += .2;
-                        if (tutoriallevel != 1) {
+                        if (!Tutorial::active) {
                             emit_sound_at(landsound2, victim->coords, 128.);
                         }
                         XYZ relative;
@@ -3280,7 +3288,7 @@ void Person::DoAnimations()
                             victim->animTarget = staggerbackhighanim;
                             victim->targetyaw = targetyaw + 180;
                             victim->target = 0;
-                            if (tutoriallevel != 1) {
+                            if (!Tutorial::active) {
                                 emit_sound_at(landsound2, victim->coords, 128.);
                             }
                             victim->Puff(abdomen);
@@ -3301,7 +3309,7 @@ void Person::DoAnimations()
                         victim->spurt = 1;
                         DoBlood(.2, 230);
                     }
-                    if (tutoriallevel != 1) {
+                    if (!Tutorial::active) {
                         emit_sound_at(heavyimpactsound, victim->coords, 128.);
                     }
                     if (creature == wolftype) {
@@ -3370,7 +3378,7 @@ void Person::DoAnimations()
 
                     award_bonus(id, staffreversebonus);
 
-                    if (tutoriallevel != 1) {
+                    if (!Tutorial::active) {
                         emit_sound_at(heavyimpactsound, victim->coords, 128.);
                     }
                     victim->RagDoll(0);
@@ -3469,7 +3477,7 @@ void Person::DoAnimations()
                         victim->spurt = 1;
                         DoBlood(.2, 230);
                     }
-                    if (tutoriallevel != 1) {
+                    if (!Tutorial::active) {
                         emit_sound_at(heavyimpactsound, victim->coords, 128.);
                     }
                     victim->RagDoll(0);
@@ -3662,7 +3670,7 @@ void Person::DoAnimations()
                         DoBlood(.2, 240);
                     }
                     if (weaponactive == -1) {
-                        if (tutoriallevel != 1) {
+                        if (!Tutorial::active) {
                             emit_sound_at(heavyimpactsound, victim->coords, 128.);
                         }
                     }
@@ -3811,7 +3819,6 @@ void Person::DoAnimations()
                     if (!isnormal(coords.x))
                         coords = oldcoords;
                     oldcoords = coords;
-                    collided = 0;
                     targetoffset = 0;
                     currentoffset = 0;
                     grabdelay = 1;
@@ -4284,7 +4291,7 @@ void Person::DoStuff()
     static XYZ flatfacing;
     static XYZ flatvelocity;
     static float flatvelspeed;
-    static int i, l;
+    static int l;
     static int bloodsize;
     static int startx, starty, endx, endy;
     static GLubyte color;
@@ -4376,7 +4383,7 @@ void Person::DoStuff()
             vel[2] = velocity.z;
 
             if (id == 0) {
-                OPENAL_3D_SetAttributes(channels[whooshsound], gLoc, vel);
+                OPENAL_3D_SetAttributes(channels[whooshsound], gLoc);
                 OPENAL_SetVolume(channels[whooshsound], 64 * findLength(&velocity) / 5);
             }
         }
@@ -4394,7 +4401,7 @@ void Person::DoStuff()
         Sprite::MakeSprite(flamesprite, flatfacing, flatvelocity, 1, 1, 1, .6 + (float)abs(Random() % 100) / 200 - .25, 1);
     }
 
-    while (flamedelay < 0 && !onfire && tutoriallevel == 1 && id != 0) {
+    while (flamedelay < 0 && !onfire && Tutorial::active && id != 0) {
         flamedelay += .05;
         int howmany = fabs(Random() % (skeleton.joints.size()));
         if (skeleton.free) {
@@ -4534,7 +4541,7 @@ void Person::DoStuff()
         if (endy < starty)
             endy = starty;
 
-        for (i = startx; i < endx; i++) {
+        for (int i = startx; i < endx; i++) {
             for (int j = starty; j < endy; j++) {
                 if (Random() % 2 == 0) {
                     color = Random() % 85 + 170;
@@ -4800,8 +4807,9 @@ void Person::DoStuff()
         }
     }
 
-    if (dead != 1)
+    if (dead != 1) {
         unconscioustime = 0;
+    }
 
     if (dead == 1 || howactive == typesleeping) {
         unconscioustime += multiplier;
@@ -4902,13 +4910,12 @@ void Person::DoStuff()
         damage += 20;
     }
 
-    if (!dead)
+    if (!dead) {
         damage -= multiplier * 13;
-    if (!dead)
         permanentdamage -= multiplier * 4;
-    if (isIdle() || isCrouch()) {
-        if (!dead)
+        if (isIdle() || isCrouch()) {
             permanentdamage -= multiplier * 4;
+        }
     }
     if (damage < 0)
         damage = 0;
@@ -5082,7 +5089,7 @@ void Person::DoStuff()
                 canrecover = 0;
             if (velocity.y < -30)
                 canrecover = 0;
-            for (i = 0; i < Object::objects.size(); i++) {
+            for (unsigned int i = 0; i < Object::objects.size(); i++) {
                 if (Object::objects[i]->type != treeleavestype && Object::objects[i]->type != bushtype && Object::objects[i]->type != firetype) {
                     colviewer = startpoint;
                     coltarget = endpoint;
@@ -5274,7 +5281,7 @@ void Person::DoStuff()
 
     if (aitype != passivetype || skeleton.free == 1)
         if (findLengthfast(&velocity) > .1)
-            for (i = 0; i < Object::objects.size(); i++) {
+            for (unsigned int i = 0; i < Object::objects.size(); i++) {
                 if (Object::objects[i]->type == firetype)
                     if (distsqflat(&coords, &Object::objects[i]->position) < Object::objects[i]->scale*Object::objects[i]->scale * 12 && distsq(&coords, &Object::objects[i]->position) < Object::objects[i]->scale*Object::objects[i]->scale * 49) {
                         if (onfire) {
@@ -5423,12 +5430,12 @@ void Person::DoStuff()
         if (hasvictim)
             if (aitype != passivetype && victim->skeleton.free && !victim->dead)
                 play = 1;
-        if (tutoriallevel == 1 && id != 0)
+        if (Tutorial::active && id != 0)
             play = 0;
         if (play && aitype != playercontrolled) {
             int whichsound = -1;
-            i = abs(Random() % 4);
             if (speechdelay <= 0) {
+                unsigned int i = abs(Random() % 4);
                 if (creature == rabbittype) {
                     if (i == 0)
                         whichsound = rabbitchitter;
@@ -5544,15 +5551,46 @@ void Person::DoStuff()
             }
         }
 
-        if (animTarget == spinkickanim || animTarget == staffspinhitreversalanim || animTarget == staffspinhitreversedanim || animTarget == staffhitreversalanim || animTarget == staffhitreversedanim || animTarget == hurtidleanim || animTarget == winduppunchanim || animTarget == swordslashreversalanim || animTarget == swordslashreversedanim || animTarget == knifeslashreversalanim || animTarget == knifeslashreversedanim || animTarget == knifethrowanim || animTarget == knifefollowanim || animTarget == knifefollowedanim || animTarget == killanim || animTarget == dropkickanim || animTarget == upunchanim || animTarget == knifeslashstartanim || animTarget == swordslashanim || animTarget == staffhitanim || animTarget == staffspinhitanim || animTarget == staffgroundsmashanim || animTarget == spinkickreversalanim || animTarget == sweepreversalanim || animTarget == lowkickanim || animTarget == sweepreversedanim || animTarget == rabbitkickreversalanim || animTarget == rabbitkickreversedanim || animTarget == jumpreversalanim || animTarget == jumpreversedanim) {
+        if (animTarget == spinkickanim ||
+           animTarget == staffspinhitreversalanim ||
+           animTarget == staffspinhitreversedanim ||
+           animTarget == staffhitreversalanim ||
+           animTarget == staffhitreversedanim ||
+           animTarget == hurtidleanim ||
+           animTarget == winduppunchanim ||
+           animTarget == swordslashreversalanim ||
+           animTarget == swordslashreversedanim ||
+           animTarget == knifeslashreversalanim ||
+           animTarget == knifeslashreversedanim ||
+           animTarget == knifethrowanim ||
+           animTarget == knifefollowanim ||
+           animTarget == knifefollowedanim ||
+           animTarget == killanim ||
+           animTarget == dropkickanim ||
+           animTarget == upunchanim ||
+           animTarget == knifeslashstartanim ||
+           animTarget == swordslashanim ||
+           animTarget == staffhitanim ||
+           animTarget == staffspinhitanim ||
+           animTarget == staffgroundsmashanim ||
+           animTarget == spinkickreversalanim ||
+           animTarget == sweepreversalanim ||
+           animTarget == lowkickanim ||
+           animTarget == sweepreversedanim ||
+           animTarget == rabbitkickreversalanim ||
+           animTarget == rabbitkickreversedanim ||
+           animTarget == jumpreversalanim ||
+           animTarget == jumpreversedanim) {
             //close hands and yell
-            if (righthandmorphend != 1 && righthandmorphness == targetrighthandmorphness) {
+            if (righthandmorphend != 1 &&
+               righthandmorphness == targetrighthandmorphness) {
                 righthandmorphness = 0;
                 righthandmorphend = 1;
                 targetrighthandmorphness = 1;
             }
 
-            if (lefthandmorphend != 1 && lefthandmorphness == targetlefthandmorphness) {
+            if (lefthandmorphend != 1 &&
+               lefthandmorphness == targetlefthandmorphness) {
                 lefthandmorphness = 0;
                 lefthandmorphend = 1;
                 targetlefthandmorphness = 1;
@@ -5612,7 +5650,8 @@ void Person::DoStuff()
         ReflectVector(&facing, terrainnormal);
         Normalise(&facing);
 
-        if (isRun() || animTarget == sneakanim || animTarget == rollanim || animTarget == walkanim) {
+        if (isRun() ||
+           animTarget == sneakanim || animTarget == rollanim || animTarget == walkanim) {
             if (onterrain)
                 targettilt2 = -facing.y * 20;
             else
@@ -5877,8 +5916,9 @@ void Person::DoStuff()
             velocity.y = 0;
             if (velspeed < multiplier * 300 * scale) {
                 velocity = 0;
-            } else
+            } else {
                 velocity -= velocity / velspeed * multiplier * 300 * scale;
+            }
             if (velspeed > 5 && (isLanding() || isLandhard())) {
                 skiddingdelay += multiplier;
                 if (skiddelay <= 0) {
@@ -5886,17 +5926,13 @@ void Person::DoStuff()
                     FootLand(rightfoot, .5);
                     skiddelay = .02;
                 }
-            } else
+            } else {
                 skiddingdelay = 0;
+            }
         }
 
         if (isLandhard()) {
             velspeed = findLength(&velocity);
-            velocity.y = 0;
-            if (velspeed < multiplier * 600 * scale) {
-                velocity = 0;
-            } else
-                velocity -= velocity / velspeed * multiplier * 600 * scale;
             velocity = 0;
             if (velspeed > 5 && (isLanding() || isLandhard())) {
                 skiddingdelay += multiplier;
@@ -5905,8 +5941,9 @@ void Person::DoStuff()
                     FootLand(rightfoot, .5);
                     skiddelay = .02;
                 }
-            } else
+            } else {
                 skiddingdelay = 0;
+            }
         }
 
         if (skiddingdelay < 0)
@@ -6053,7 +6090,7 @@ int Person::DrawSkeleton()
         }
         static XYZ mid;
         static float M[16];
-        static int i, k;
+        static int k;
         static int weaponattachmuscle;
         static int weaponrotatemuscle;
         static XYZ weaponpoint;
@@ -6093,19 +6130,19 @@ int Person::DrawSkeleton()
                 if (Animation::animations[animTarget].attack == 3)
                     targetheadyaw += 180;
             }
-            for (i = 0; i < skeleton.drawmodel.vertexNum; i++) {
+            for (int i = 0; i < skeleton.drawmodel.vertexNum; i++) {
                 skeleton.drawmodel.vertex[i] = 0;
                 skeleton.drawmodel.vertex[i].y = 999;
             }
-            for (i = 0; i < skeleton.drawmodellow.vertexNum; i++) {
+            for (int i = 0; i < skeleton.drawmodellow.vertexNum; i++) {
                 skeleton.drawmodellow.vertex[i] = 0;
                 skeleton.drawmodellow.vertex[i].y = 999;
             }
-            for (i = 0; i < skeleton.drawmodelclothes.vertexNum; i++) {
+            for (int i = 0; i < skeleton.drawmodelclothes.vertexNum; i++) {
                 skeleton.drawmodelclothes.vertex[i] = 0;
                 skeleton.drawmodelclothes.vertex[i].y = 999;
             }
-            for (unsigned i = 0; i < skeleton.muscles.size(); i++) {
+            for (unsigned int i = 0; i < skeleton.muscles.size(); i++) {
                 // convenience renames
                 const int p1 = skeleton.muscles[i].parent1->label;
                 const int p2 = skeleton.muscles[i].parent2->label;
@@ -6316,16 +6353,17 @@ int Person::DrawSkeleton()
             glDisable(GL_LIGHTING);
             glDisable(GL_TEXTURE_2D);
             glBegin(GL_POINTS);
-            if (playerdetail)
-                for (i = 0; i < skeleton.drawmodel.vertexNum; i++) {
+            if (playerdetail) {
+                for (int i = 0; i < skeleton.drawmodel.vertexNum; i++) {
                     XYZ &v0 = skeleton.drawmodel.vertex[i];
                     glVertex3f(v0.x, v0.y, v0.z);
                 }
+            }
             glEnd();
             glBegin(GL_LINES);
 
-            if (playerdetail)
-                for (i = 0; i < skeleton.drawmodel.TriangleNum; i++) {
+            if (playerdetail) {
+                for (unsigned int i = 0; i < skeleton.drawmodel.Triangles.size(); i++) {
                     XYZ &v0 = skeleton.drawmodel.vertex[skeleton.drawmodel.Triangles[i].vertex[0]];
                     XYZ &v1 = skeleton.drawmodel.vertex[skeleton.drawmodel.Triangles[i].vertex[1]];
                     XYZ &v2 = skeleton.drawmodel.vertex[skeleton.drawmodel.Triangles[i].vertex[2]];
@@ -6336,6 +6374,7 @@ int Person::DrawSkeleton()
                     glVertex3f(v2.x, v2.y, v2.z);
                     glVertex3f(v0.x, v0.y, v0.z);
                 }
+            }
 
             glEnd();
         }
@@ -6363,7 +6402,7 @@ int Person::DrawSkeleton()
                 glEnable(GL_LIGHTING);
                 glEnable(GL_BLEND);
             }
-            if (tutoriallevel && id != 0) {
+            if (Tutorial::active && id != 0) {
                 glColor4f(.7, .7, .7, 0.6);
                 glDepthMask(0);
                 glEnable(GL_LIGHTING);
@@ -6380,21 +6419,21 @@ int Person::DrawSkeleton()
             }
             if (playerdetail) {
                 if (!showpoints) {
-                    if ((tutoriallevel && id != 0))
+                    if (Tutorial::active && (id != 0))
                         skeleton.drawmodel.drawdifftex(Sprite::cloudimpacttexture);
                     else
                         skeleton.drawmodel.draw();
                 }
             }
             if (!playerdetail) {
-                if ((tutoriallevel && id != 0))
+                if (Tutorial::active && (id != 0))
                     skeleton.drawmodellow.drawdifftex(Sprite::cloudimpacttexture);
                 else
                     skeleton.drawmodellow.drawdifftex(skeleton.drawmodel.textureptr);
             }
 
             if (!(Animation::animations[animTarget].attack == normalattack || Animation::animations[animTarget].attack == reversed))
-                if (tutoriallevel && id != 0) {
+                if (Tutorial::active && id != 0) {
                     glPopMatrix();
                     glMatrixMode(GL_MODELVIEW);
                     glEnable(GL_TEXTURE_2D);
@@ -6413,14 +6452,14 @@ int Person::DrawSkeleton()
                     glTranslatef(smoketex * .6, 0, 0);
                     if (playerdetail) {
                         if (!showpoints) {
-                            if ((tutoriallevel && id != 0))
+                            if (Tutorial::active && (id != 0))
                                 skeleton.drawmodel.drawdifftex(Sprite::cloudimpacttexture);
                             else
                                 skeleton.drawmodel.draw();
                         }
                     }
                     if (!playerdetail) {
-                        if ((tutoriallevel && id != 0))
+                        if (Tutorial::active && (id != 0))
                             skeleton.drawmodellow.drawdifftex(Sprite::cloudimpacttexture);
                         else
                             skeleton.drawmodellow.drawdifftex(skeleton.drawmodel.textureptr);
@@ -6428,7 +6467,7 @@ int Person::DrawSkeleton()
                 }
 
 
-            if (tutoriallevel && id != 0) {
+            if (Tutorial::active && id != 0) {
                 glPopMatrix();
                 glMatrixMode(GL_MODELVIEW);
                 glEnable(GL_TEXTURE_2D);
@@ -6447,7 +6486,7 @@ int Person::DrawSkeleton()
 
         if (num_weapons > 0) {
             for (k = 0; k < num_weapons; k++) {
-                i = weaponids[k];
+                int i = weaponids[k];
                 if (weaponactive == k) {
                     if (weapons[i].getType() != staff) {
                         for (unsigned j = 0; j < skeleton.muscles.size(); j++) {
@@ -6685,7 +6724,6 @@ int Person::DrawSkeleton()
  */
 int Person::SphereCheck(XYZ *p1, float radius, XYZ *p, XYZ *move, float *rotate, Model *model)
 {
-    static int i, j;
     static float distance;
     static float olddistance;
     static int intersecting;
@@ -6703,14 +6741,14 @@ int Person::SphereCheck(XYZ *p1, float radius, XYZ *p, XYZ *move, float *rotate,
         return -1;
     if (*rotate)
         *p1 = DoRotation(*p1, 0, -*rotate, 0);
-    for (i = 0; i < 4; i++) {
-        for (j = 0; j < model->TriangleNum; j++) {
-            if (model->facenormals[j].y <= slopethreshold) {
+    for (int i = 0; i < 4; i++) {
+        for (unsigned int j = 0; j < model->Triangles.size(); j++) {
+            if (model->Triangles[j].facenormal.y <= slopethreshold) {
                 intersecting = 0;
-                distance = abs((model->facenormals[j].x * p1->x) + (model->facenormals[j].y * p1->y) + (model->facenormals[j].z * p1->z) - ((model->facenormals[j].x * model->vertex[model->Triangles[j].vertex[0]].x) + (model->facenormals[j].y * model->vertex[model->Triangles[j].vertex[0]].y) + (model->facenormals[j].z * model->vertex[model->Triangles[j].vertex[0]].z)));
+                distance = abs((model->Triangles[j].facenormal.x * p1->x) + (model->Triangles[j].facenormal.y * p1->y) + (model->Triangles[j].facenormal.z * p1->z) - ((model->Triangles[j].facenormal.x * model->vertex[model->Triangles[j].vertex[0]].x) + (model->Triangles[j].facenormal.y * model->vertex[model->Triangles[j].vertex[0]].y) + (model->Triangles[j].facenormal.z * model->vertex[model->Triangles[j].vertex[0]].z)));
                 if (distance < radius) {
-                    point = *p1 - model->facenormals[j] * distance;
-                    if (PointInTriangle( &point, model->facenormals[j], &model->vertex[model->Triangles[j].vertex[0]], &model->vertex[model->Triangles[j].vertex[1]], &model->vertex[model->Triangles[j].vertex[2]]))
+                    point = *p1 - model->Triangles[j].facenormal * distance;
+                    if (PointInTriangle( &point, model->Triangles[j].facenormal, &model->vertex[model->Triangles[j].vertex[0]], &model->vertex[model->Triangles[j].vertex[1]], &model->vertex[model->Triangles[j].vertex[2]]))
                         intersecting = 1;
                     if (!intersecting)
                         intersecting = sphere_line_intersection(&model->vertex[model->Triangles[j].vertex[0]],
@@ -6725,11 +6763,11 @@ int Person::SphereCheck(XYZ *p1, float radius, XYZ *p, XYZ *move, float *rotate,
                                                                 &model->vertex[model->Triangles[j].vertex[2]],
                                                                 p1, &radius);
                     end = *p1 - point;
-                    if (dotproduct(&model->facenormals[j], &end) > 0 && intersecting) {
+                    if (dotproduct(&model->Triangles[j].facenormal, &end) > 0 && intersecting) {
                         start = *p1;
                         end = *p1;
                         end.y -= radius;
-                        if (LineFacetd(&start, &end, &model->vertex[model->Triangles[j].vertex[0]], &model->vertex[model->Triangles[j].vertex[1]], &model->vertex[model->Triangles[j].vertex[2]], &model->facenormals[j], &point)) {
+                        if (LineFacetd(&start, &end, &model->vertex[model->Triangles[j].vertex[0]], &model->vertex[model->Triangles[j].vertex[1]], &model->vertex[model->Triangles[j].vertex[2]], &model->Triangles[j].facenormal, &point)) {
                             p1->y = point.y + radius;
                             if ((animTarget == jumpdownanim || isFlip())) {
                                 if (isFlip() && (frameTarget < 5 || targetFrame().label == 7 || targetFrame().label == 4))
@@ -6769,23 +6807,23 @@ int Person::SphereCheck(XYZ *p1, float radius, XYZ *p, XYZ *move, float *rotate,
                 }
             }
         }
-        for (j = 0; j < model->TriangleNum; j++) {
-            if (model->facenormals[j].y > slopethreshold) {
+        for (unsigned int j = 0; j < model->Triangles.size(); j++) {
+            if (model->Triangles[j].facenormal.y > slopethreshold) {
                 intersecting = 0;
                 start = *p1;
                 start.y -= radius / 4;
                 XYZ &v0 = model->vertex[model->Triangles[j].vertex[0]];
                 XYZ &v1 = model->vertex[model->Triangles[j].vertex[1]];
                 XYZ &v2 = model->vertex[model->Triangles[j].vertex[2]];
-                distance = abs((model->facenormals[j].x * start.x)
-                               + (model->facenormals[j].y * start.y)
-                               + (model->facenormals[j].z * start.z)
-                               - ((model->facenormals[j].x * v0.x)
-                                  + (model->facenormals[j].y * v0.y)
-                                  + (model->facenormals[j].z * v0.z)));
+                distance = abs((model->Triangles[j].facenormal.x * start.x)
+                               + (model->Triangles[j].facenormal.y * start.y)
+                               + (model->Triangles[j].facenormal.z * start.z)
+                               - ((model->Triangles[j].facenormal.x * v0.x)
+                                  + (model->Triangles[j].facenormal.y * v0.y)
+                                  + (model->Triangles[j].facenormal.z * v0.z)));
                 if (distance < radius * .5) {
-                    point = start - model->facenormals[j] * distance;
-                    if (PointInTriangle( &point, model->facenormals[j], &v0, &v1, &v2))
+                    point = start - model->Triangles[j].facenormal * distance;
+                    if (PointInTriangle( &point, model->Triangles[j].facenormal, &v0, &v1, &v2))
                         intersecting = 1;
                     if (!intersecting)
                         intersecting = sphere_line_intersection(v0.x, v0.y, v0.z, v1.x, v1.y, v1.z, p1->x, p1->y, p1->z, radius / 2);
@@ -6794,14 +6832,14 @@ int Person::SphereCheck(XYZ *p1, float radius, XYZ *p, XYZ *move, float *rotate,
                     if (!intersecting)
                         intersecting = sphere_line_intersection(v0.x, v0.y, v0.z, v2.x, v2.y, v2.z, p1->x, p1->y, p1->z, radius / 2);
                     end = *p1 - point;
-                    if (dotproduct(&model->facenormals[j], &end) > 0 && intersecting) {
+                    if (dotproduct(&model->Triangles[j].facenormal, &end) > 0 && intersecting) {
                         if ((animTarget == jumpdownanim || animTarget == jumpupanim || isFlip())) {
                             start = velocity;
-                            velocity -= DoRotation(model->facenormals[j], 0, *rotate, 0) * findLength(&velocity) * abs(normaldotproduct(velocity, DoRotation(model->facenormals[j], 0, *rotate, 0))); //(distance-radius*.5)/multiplier;
+                            velocity -= DoRotation(model->Triangles[j].facenormal, 0, *rotate, 0) * findLength(&velocity) * abs(normaldotproduct(velocity, DoRotation(model->Triangles[j].facenormal, 0, *rotate, 0))); //(distance-radius*.5)/multiplier;
                             if (findLengthfast(&start) < findLengthfast(&velocity))
                                 velocity = start;
                         }
-                        *p1 += model->facenormals[j] * (distance - radius * .5);
+                        *p1 += model->Triangles[j].facenormal * (distance - radius * .5);
                     }
                 }
                 if ((distance < olddistance || firstintersecting == -1) && intersecting) {
@@ -6821,6 +6859,51 @@ int Person::SphereCheck(XYZ *p1, float radius, XYZ *p, XYZ *move, float *rotate,
     return firstintersecting;
 }
 
+int findPathDist(int start, int end)
+{
+    int connected;
+    int closest;
+
+    unsigned int smallestcount = 1000;
+    for (char i = 0; i < 50; i++) {
+        unsigned int count = 0;
+        int last = start;
+        int last2 = -1;
+        int last3 = -1;
+        int last4 = -1;
+        while (last != end && count < 30) {
+            closest = -1;
+            for (int j = 0; j < Game::numpathpoints; j++) {
+                if (j != last && j != last2 && j != last3 && j != last4) {
+                    connected = 0;
+                    if (Game::numpathpointconnect[j])
+                        for (int k = 0; k < Game::numpathpointconnect[j]; k++) {
+                            if (Game::pathpointconnect[j][k] == last)connected = 1;
+                        }
+                    if (!connected)
+                        if (Game::numpathpointconnect[last])
+                            for (int k = 0; k < Game::numpathpointconnect[last]; k++) {
+                                if (Game::pathpointconnect[last][k] == j)connected = 1;
+                            }
+                    if (connected)
+                        if (closest == -1 || Random() % 2 == 0) {
+                            closest = j;
+                        }
+                }
+            }
+            last4 = last3;
+            last3 = last2;
+            last2 = last;
+            last = closest;
+            count++;
+        }
+        if (count < smallestcount) {
+            smallestcount = count;
+        }
+    }
+    return smallestcount;
+}
+
 void Person::takeWeapon(int weaponId)
 {
     weaponactive = 0;
@@ -6893,3 +6976,985 @@ bool Person::addClothes(const int& clothesId)
         return 0;
     }
 }
+
+void Person::doAI()
+{
+    if (aitype != playercontrolled && !Dialog::inDialog()) {
+        jumpclimb = 0;
+        //disable movement in editor
+        if (Game::editorenabled)
+            stunned = 1;
+
+        pause = 0;
+        if (distsqflat(&Person::players[0]->coords, &coords) < 30 &&
+                Person::players[0]->coords.y > coords.y + 2 &&
+                !Person::players[0]->onterrain)
+            pause = 1;
+
+        //pathfinding
+        if (aitype == pathfindtype) {
+            if (finalpathfindpoint == -1) {
+                float closestdistance;
+                float tempdist;
+                int closest;
+                XYZ colpoint;
+                closest = -1;
+                closestdistance = -1;
+                for (int j = 0; j < Game::numpathpoints; j++) {
+                    if (closest == -1 || distsq(&finalfinaltarget, &Game::pathpoint[j]) < closestdistance) {
+                        closestdistance = distsq(&finalfinaltarget, &Game::pathpoint[j]);
+                        closest = j;
+                        finaltarget = Game::pathpoint[j];
+                    }
+                }
+                finalpathfindpoint = closest;
+                for (int j = 0; j < Game::numpathpoints; j++) {
+                    for (int k = 0; k < Game::numpathpointconnect[j]; k++) {
+                        DistancePointLine(&finalfinaltarget, &Game::pathpoint[j], &Game::pathpoint[Game::pathpointconnect[j][k]], &tempdist, &colpoint);
+                        if (sq(tempdist) < closestdistance)
+                            if (findDistance(&colpoint, &Game::pathpoint[j]) + findDistance(&colpoint, &Game::pathpoint[Game::pathpointconnect[j][k]]) <
+                                    findDistance(&Game::pathpoint[j], &Game::pathpoint[Game::pathpointconnect[j][k]]) + .1) {
+                                closestdistance = sq(tempdist);
+                                closest = j;
+                                finaltarget = colpoint;
+                            }
+                    }
+                }
+                finalpathfindpoint = closest;
+
+            }
+            if (targetpathfindpoint == -1) {
+                float closestdistance;
+                float tempdist;
+                int closest;
+                XYZ colpoint;
+                closest = -1;
+                closestdistance = -1;
+                if (lastpathfindpoint == -1) {
+                    for (int j = 0; j < Game::numpathpoints; j++) {
+                        if (j != lastpathfindpoint)
+                            if (closest == -1 || (distsq(&coords, &Game::pathpoint[j]) < closestdistance)) {
+                                closestdistance = distsq(&coords, &Game::pathpoint[j]);
+                                closest = j;
+                            }
+                    }
+                    targetpathfindpoint = closest;
+                    for (int j = 0; j < Game::numpathpoints; j++)
+                        if (j != lastpathfindpoint)
+                            for (int k = 0; k < Game::numpathpointconnect[j]; k++) {
+                                DistancePointLine(&coords, &Game::pathpoint[j], &Game::pathpoint[Game::pathpointconnect[j][k]], &tempdist, &colpoint );
+                                if (sq(tempdist) < closestdistance) {
+                                    if (findDistance(&colpoint, &Game::pathpoint[j]) + findDistance(&colpoint, &Game::pathpoint[Game::pathpointconnect[j][k]]) <
+                                            findDistance(&Game::pathpoint[j], &Game::pathpoint[Game::pathpointconnect[j][k]]) + .1) {
+                                        closestdistance = sq(tempdist);
+                                        closest = j;
+                                    }
+                                }
+                            }
+                    targetpathfindpoint = closest;
+                } else {
+                    for (int j = 0; j < Game::numpathpoints; j++)
+                        if (j != lastpathfindpoint &&
+                                j != lastpathfindpoint2 &&
+                                j != lastpathfindpoint3 &&
+                                j != lastpathfindpoint4) {
+                            bool connected = 0;
+                            if (Game::numpathpointconnect[j])
+                                for (int k = 0; k < Game::numpathpointconnect[j]; k++)
+                                    if (Game::pathpointconnect[j][k] == lastpathfindpoint)
+                                        connected = 1;
+                            if (!connected)
+                                if (Game::numpathpointconnect[lastpathfindpoint])
+                                    for (int k = 0; k < Game::numpathpointconnect[lastpathfindpoint]; k++)
+                                        if (Game::pathpointconnect[lastpathfindpoint][k] == j)
+                                            connected = 1;
+                            if (connected) {
+                                tempdist = findPathDist(j, finalpathfindpoint);
+                                if (closest == -1 || tempdist < closestdistance) {
+                                    closestdistance = tempdist;
+                                    closest = j;
+                                }
+                            }
+                        }
+                    targetpathfindpoint = closest;
+                }
+            }
+            losupdatedelay -= multiplier;
+
+            targetyaw = roughDirectionTo(coords, Game::pathpoint[targetpathfindpoint]);
+            lookyaw = targetyaw;
+
+            //reached target point
+            if (distsqflat(&coords, &Game::pathpoint[targetpathfindpoint]) < .6) {
+                lastpathfindpoint4 = lastpathfindpoint3;
+                lastpathfindpoint3 = lastpathfindpoint2;
+                lastpathfindpoint2 = lastpathfindpoint;
+                lastpathfindpoint = targetpathfindpoint;
+                if (lastpathfindpoint2 == -1)
+                    lastpathfindpoint2 = lastpathfindpoint;
+                if (lastpathfindpoint3 == -1)
+                    lastpathfindpoint3 = lastpathfindpoint2;
+                if (lastpathfindpoint4 == -1)
+                    lastpathfindpoint4 = lastpathfindpoint3;
+                targetpathfindpoint = -1;
+            }
+            if (     distsqflat(&coords, &finalfinaltarget) <
+                     distsqflat(&coords, &finaltarget) ||
+                     distsqflat(&coords, &finaltarget) < .6 * sq(scale * 5) ||
+                     lastpathfindpoint == finalpathfindpoint) {
+                aitype = passivetype;
+            }
+
+            forwardkeydown = 1;
+            leftkeydown = 0;
+            backkeydown = 0;
+            rightkeydown = 0;
+            crouchkeydown = 0;
+            attackkeydown = 0;
+            throwkeydown = 0;
+
+            if (avoidcollided > .8 && !jumpkeydown && collided < .8)
+                targetyaw += 90 * (whichdirection * 2 - 1);
+
+            if (collided < 1 || animTarget != jumpupanim)
+                jumpkeydown = 0;
+            if ((collided > .8 && jumppower >= 5))
+                jumpkeydown = 1;
+
+            if ((!Tutorial::active || cananger) &&
+                    hostile &&
+                    !Person::players[0]->dead &&
+                    distsq(&coords, &Person::players[0]->coords) < 400 &&
+                    occluded < 25) {
+                if (distsq(&coords, &Person::players[0]->coords) < 12 &&
+                        Animation::animations[Person::players[0]->animTarget].height != lowheight &&
+                        !Game::editorenabled &&
+                        (Person::players[0]->coords.y < coords.y + 5 || Person::players[0]->onterrain))
+                    aitype = attacktypecutoff;
+                if (distsq(&coords, &Person::players[0]->coords) < 30 &&
+                        Animation::animations[Person::players[0]->animTarget].height == highheight &&
+                        !Game::editorenabled)
+                    aitype = attacktypecutoff;
+
+                if (losupdatedelay < 0 && !Game::editorenabled && occluded < 2) {
+                    losupdatedelay = .2;
+                    for (unsigned j = 0; j < Person::players.size(); j++)
+                        if (j == 0 || Person::players[j]->skeleton.free || Person::players[j]->aitype != passivetype)
+                            if (abs(Random() % 2) || Animation::animations[Person::players[j]->animTarget].height != lowheight || j != 0)
+                                if (distsq(&coords, &Person::players[j]->coords) < 400)
+                                    if (normaldotproduct(facing, Person::players[j]->coords - coords) > 0)
+                                        if (Person::players[j]->coords.y < coords.y + 5 || Person::players[j]->onterrain)
+                                            if (!Person::players[j]->isWallJump() && -1 == Object::checkcollide(
+                                                        DoRotation(jointPos(head), 0, yaw, 0)
+                                                        *scale + coords,
+                                                        DoRotation(Person::players[j]->jointPos(head), 0, Person::players[j]->yaw, 0)
+                                                        *Person::players[j]->scale + Person::players[j]->coords) ||
+                                                    (Person::players[j]->animTarget == hanganim &&
+                                                     normaldotproduct(Person::players[j]->facing, coords - Person::players[j]->coords) < 0)) {
+                                                aitype = searchtype;
+                                                lastchecktime = 12;
+                                                lastseen = Person::players[j]->coords;
+                                                lastseentime = 12;
+                                            }
+                }
+            }
+            if (aitype == attacktypecutoff && Game::musictype != 2)
+                if (creature != wolftype) {
+                    stunned = .6;
+                    surprised = .6;
+                }
+        }
+
+        if (aitype != passivetype && Game::leveltime > .5)
+            howactive = typeactive;
+
+        if (aitype == passivetype) {
+            aiupdatedelay -= multiplier;
+            losupdatedelay -= multiplier;
+            lastseentime += multiplier;
+            pausetime -= multiplier;
+            if (lastseentime > 1)
+                lastseentime = 1;
+
+            if (aiupdatedelay < 0) {
+                if (numwaypoints > 1 && howactive == typeactive && pausetime <= 0) {
+                    targetyaw = roughDirectionTo(coords, waypoints[waypoint]);
+                    lookyaw = targetyaw;
+                    aiupdatedelay = .05;
+
+                    if (distsqflat(&coords, &waypoints[waypoint]) < 1) {
+                        if (waypointtype[waypoint] == wppause)
+                            pausetime = 4;
+                        waypoint++;
+                        if (waypoint > numwaypoints - 1)
+                            waypoint = 0;
+
+                    }
+                }
+
+                if (numwaypoints > 1 && howactive == typeactive && pausetime <= 0)
+                    forwardkeydown = 1;
+                else
+                    forwardkeydown = 0;
+                leftkeydown = 0;
+                backkeydown = 0;
+                rightkeydown = 0;
+                crouchkeydown = 0;
+                attackkeydown = 0;
+                throwkeydown = 0;
+
+                if (avoidcollided > .8 && !jumpkeydown && collided < .8) {
+                    if (!avoidsomething)
+                        targetyaw += 90 * (whichdirection * 2 - 1);
+                    else {
+                        XYZ leftpos, rightpos;
+                        float leftdist, rightdist;
+                        leftpos = coords + DoRotation(facing, 0, 90, 0);
+                        rightpos = coords - DoRotation(facing, 0, 90, 0);
+                        leftdist = distsq(&leftpos, &avoidwhere);
+                        rightdist = distsq(&rightpos, &avoidwhere);
+                        if (leftdist < rightdist)
+                            targetyaw += 90;
+                        else
+                            targetyaw -= 90;
+                    }
+                }
+            }
+            if (collided < 1 || animTarget != jumpupanim)
+                jumpkeydown = 0;
+            if ((collided > .8 && jumppower >= 5))
+                jumpkeydown = 1;
+
+
+            //hearing sounds
+            if (!Game::editorenabled) {
+                if (howactive <= typesleeping)
+                    if (numenvsounds > 0 && (!Tutorial::active || cananger) && hostile)
+                        for (int j = 0; j < numenvsounds; j++) {
+                            float vol = howactive == typesleeping ? envsoundvol[j] - 14 : envsoundvol[j];
+                            if (vol > 0 && distsq(&coords, &envsound[j]) <
+                                    2 * (vol + vol * (creature == rabbittype) * 3))
+                                aitype = attacktypecutoff;
+                        }
+
+                if (aitype != passivetype) {
+                    if (howactive == typesleeping)
+                        setTargetAnimation(getupfromfrontanim);
+                    howactive = typeactive;
+                }
+            }
+
+            if (howactive < typesleeping &&
+                    ((!Tutorial::active || cananger) && hostile) &&
+                    !Person::players[0]->dead &&
+                    distsq(&coords, &Person::players[0]->coords) < 400 &&
+                    occluded < 25) {
+                if (distsq(&coords, &Person::players[0]->coords) < 12 &&
+                        Animation::animations[Person::players[0]->animTarget].height != lowheight && !Game::editorenabled)
+                    aitype = attacktypecutoff;
+                if (distsq(&coords, &Person::players[0]->coords) < 30 &&
+                        Animation::animations[Person::players[0]->animTarget].height == highheight && !Game::editorenabled)
+                    aitype = attacktypecutoff;
+
+                //wolf smell
+                if (creature == wolftype) {
+                    XYZ windsmell;
+                    for (unsigned j = 0; j < Person::players.size(); j++) {
+                        if (j == 0 || (Person::players[j]->dead && Person::players[j]->bloodloss > 0)) {
+                            float smelldistance = 50;
+                            if (j == 0 && Person::players[j]->num_weapons > 0) {
+                                if (weapons[Person::players[j]->weaponids[0]].bloody)
+                                    smelldistance = 100;
+                                if (Person::players[j]->num_weapons == 2)
+                                    if (weapons[Person::players[j]->weaponids[1]].bloody)
+                                        smelldistance = 100;
+                            }
+                            if (j != 0)
+                                smelldistance = 100;
+                            windsmell = windvector;
+                            Normalise(&windsmell);
+                            windsmell = windsmell * 2 + Person::players[j]->coords;
+                            if (distsq(&coords, &windsmell) < smelldistance && !Game::editorenabled)
+                                aitype = attacktypecutoff;
+                        }
+                    }
+                }
+
+                if (howactive < typesleeping && losupdatedelay < 0 && !Game::editorenabled && occluded < 2) {
+                    losupdatedelay = .2;
+                    for (unsigned j = 0; j < Person::players.size(); j++) {
+                        if (j == 0 || Person::players[j]->skeleton.free || Person::players[j]->aitype != passivetype) {
+                            if (abs(Random() % 2) || Animation::animations[Person::players[j]->animTarget].height != lowheight || j != 0)
+                                if (distsq(&coords, &Person::players[j]->coords) < 400)
+                                    if (normaldotproduct(facing, Person::players[j]->coords - coords) > 0)
+                                        if ((-1 == Object::checkcollide(
+                                                    DoRotation(jointPos(head), 0, yaw, 0)*
+                                                    scale + coords,
+                                                    DoRotation(Person::players[j]->jointPos(head), 0, Person::players[j]->yaw, 0)*
+                                                    Person::players[j]->scale + Person::players[j]->coords) &&
+                                                !Person::players[j]->isWallJump()) ||
+                                                (Person::players[j]->animTarget == hanganim &&
+                                                 normaldotproduct(Person::players[j]->facing, coords - Person::players[j]->coords) < 0)) {
+                                            lastseentime -= .2;
+                                            if (j == 0 && Animation::animations[Person::players[j]->animTarget].height == lowheight)
+                                                lastseentime -= .4;
+                                            else
+                                                lastseentime -= .6;
+                                        }
+                            if (lastseentime <= 0) {
+                                aitype = searchtype;
+                                lastchecktime = 12;
+                                lastseen = Person::players[j]->coords;
+                                lastseentime = 12;
+                            }
+                        }
+                    }
+                }
+            }
+            //alerted surprise
+            if (aitype == attacktypecutoff && Game::musictype != 2) {
+                if (creature != wolftype) {
+                    stunned = .6;
+                    surprised = .6;
+                }
+                if (creature == wolftype) {
+                    stunned = .47;
+                    surprised = .47;
+                }
+                numseen++;
+            }
+        }
+
+        //search for player
+        int j;
+        if (aitype == searchtype) {
+            aiupdatedelay -= multiplier;
+            losupdatedelay -= multiplier;
+            if (!pause)
+                lastseentime -= multiplier;
+            lastchecktime -= multiplier;
+
+            if (isRun() && !onground) {
+                if (coords.y > terrain.getHeight(coords.x, coords.z) + 10) {
+                    XYZ test2 = coords + facing;
+                    test2.y += 5;
+                    XYZ test = coords + facing;
+                    test.y -= 10;
+                    j = Object::checkcollide(test2, test, laststanding);
+                    if (j == -1)
+                        j = Object::checkcollide(test2, test);
+                    if (j == -1) {
+                        velocity = 0;
+                        setTargetAnimation(getStop());
+                        targetyaw += 180;
+                        stunned = .5;
+                        //aitype=passivetype;
+                        aitype = pathfindtype;
+                        finalfinaltarget = waypoints[waypoint];
+                        finalpathfindpoint = -1;
+                        targetpathfindpoint = -1;
+                        lastpathfindpoint = -1;
+                        lastpathfindpoint2 = -1;
+                        lastpathfindpoint3 = -1;
+                        lastpathfindpoint4 = -1;
+                    } else
+                        laststanding = j;
+                }
+            }
+            //check out last seen location
+            if (aiupdatedelay < 0) {
+                targetyaw = roughDirectionTo(coords, lastseen);
+                lookyaw = targetyaw;
+                aiupdatedelay = .05;
+                forwardkeydown = 1;
+
+                if (distsqflat(&coords, &lastseen) < 1 * sq(scale * 5) || lastchecktime < 0) {
+                    forwardkeydown = 0;
+                    aiupdatedelay = 1;
+                    lastseen.x += (float(Random() % 100) - 50) / 25;
+                    lastseen.z += (float(Random() % 100) - 50) / 25;
+                    lastchecktime = 3;
+                }
+
+                leftkeydown = 0;
+                backkeydown = 0;
+                rightkeydown = 0;
+                crouchkeydown = 0;
+                attackkeydown = 0;
+                throwkeydown = 0;
+
+                if (avoidcollided > .8 && !jumpkeydown && collided < .8) {
+                    if (!avoidsomething)
+                        targetyaw += 90 * (whichdirection * 2 - 1);
+                    else {
+                        XYZ leftpos, rightpos;
+                        float leftdist, rightdist;
+                        leftpos = coords + DoRotation(facing, 0, 90, 0);
+                        rightpos = coords - DoRotation(facing, 0, 90, 0);
+                        leftdist = distsq(&leftpos, &avoidwhere);
+                        rightdist = distsq(&rightpos, &avoidwhere);
+                        if (leftdist < rightdist)
+                            targetyaw += 90;
+                        else
+                            targetyaw -= 90;
+                    }
+                }
+            }
+            if (collided < 1 || animTarget != jumpupanim)
+                jumpkeydown = 0;
+            if ((collided > .8 && jumppower >= 5))
+                jumpkeydown = 1;
+
+            if (numenvsounds > 0 && ((!Tutorial::active || cananger) && hostile))
+                for (int k = 0; k < numenvsounds; k++) {
+                    if (distsq(&coords, &envsound[k]) < 2 * (envsoundvol[k] + envsoundvol[k] * (creature == rabbittype) * 3)) {
+                        aitype = attacktypecutoff;
+                    }
+                }
+
+            if (!Person::players[0]->dead &&
+                    losupdatedelay < 0 &&
+                    !Game::editorenabled &&
+                    occluded < 2 &&
+                    ((!Tutorial::active || cananger) && hostile)) {
+                losupdatedelay = .2;
+                if (distsq(&coords, &Person::players[0]->coords) < 4 && Animation::animations[animTarget].height != lowheight) {
+                    aitype = attacktypecutoff;
+                    lastseentime = 1;
+                }
+                if (abs(Random() % 2) || Animation::animations[animTarget].height != lowheight)
+                    //TODO: factor out canSeePlayer()
+                    if (distsq(&coords, &Person::players[0]->coords) < 400)
+                        if (normaldotproduct(facing, Person::players[0]->coords - coords) > 0)
+                            if ((Object::checkcollide(
+                                        DoRotation(jointPos(head), 0, yaw, 0)*
+                                        scale + coords,
+                                        DoRotation(Person::players[0]->jointPos(head), 0, Person::players[0]->yaw, 0)*
+                                        Person::players[0]->scale + Person::players[0]->coords) == -1) ||
+                                    (Person::players[0]->animTarget == hanganim && normaldotproduct(
+                                         Person::players[0]->facing, coords - Person::players[0]->coords) < 0)) {
+                                /* //TODO: changed j to 0 on a whim, make sure this is correct
+                                (Person::players[j]->animTarget==hanganim&&normaldotproduct(
+                                    Person::players[j]->facing,coords-Person::players[j]->coords)<0)
+                                */
+                                aitype = attacktypecutoff;
+                                lastseentime = 1;
+                            }
+            }
+            //player escaped
+            if (lastseentime < 0) {
+                //aitype=passivetype;
+                numescaped++;
+                aitype = pathfindtype;
+                finalfinaltarget = waypoints[waypoint];
+                finalpathfindpoint = -1;
+                targetpathfindpoint = -1;
+                lastpathfindpoint = -1;
+                lastpathfindpoint2 = -1;
+                lastpathfindpoint3 = -1;
+                lastpathfindpoint4 = -1;
+            }
+        }
+
+        if (aitype != gethelptype)
+            runninghowlong = 0;
+
+        //get help from buddies
+        if (aitype == gethelptype) {
+            runninghowlong += multiplier;
+            aiupdatedelay -= multiplier;
+
+            if (aiupdatedelay < 0 || ally == 0) {
+                aiupdatedelay = .2;
+
+                //find closest ally
+                //TODO: factor out closest search somehow
+                if (!ally) {
+                    int closest = -1;
+                    float closestdist = -1;
+                    for (unsigned k = 0; k < Person::players.size(); k++) {
+                        if ((k != id) && (k != 0) && !Person::players[k]->dead &&
+                                (Person::players[k]->howactive < typedead1) &&
+                                !Person::players[k]->skeleton.free &&
+                                (Person::players[k]->aitype == passivetype)) {
+                            float distance = distsq(&coords, &Person::players[k]->coords);
+                            if (closestdist == -1 || distance < closestdist) {
+                                closestdist = distance;
+                                closest = k;
+                            }
+                            closest = k;
+                        }
+                    }
+                    if (closest != -1) {
+                        ally = closest;
+                    } else {
+                        ally = 0;
+                    }
+                    lastseen = Person::players[0]->coords;
+                    lastseentime = 12;
+                }
+
+
+                lastchecktime = 12;
+
+                XYZ facing = coords;
+                XYZ flatfacing = Person::players[ally]->coords;
+                facing.y += jointPos(head).y * scale;
+                flatfacing.y += Person::players[ally]->jointPos(head).y * Person::players[ally]->scale;
+                if (-1 != Object::checkcollide(facing, flatfacing))
+                    lastseentime -= .1;
+
+                //no available ally, run back to player
+                if (ally <= 0 ||
+                        Person::players[ally]->skeleton.free ||
+                        Person::players[ally]->aitype != passivetype ||
+                        lastseentime <= 0) {
+                    aitype = searchtype;
+                    lastseentime = 12;
+                }
+
+                //seek out ally
+                if (ally > 0) {
+                    targetyaw = roughDirectionTo(coords, Person::players[ally]->coords);
+                    lookyaw = targetyaw;
+                    aiupdatedelay = .05;
+                    forwardkeydown = 1;
+
+                    if (distsqflat(&coords, &Person::players[ally]->coords) < 3) {
+                        aitype = searchtype;
+                        lastseentime = 12;
+                        Person::players[ally]->aitype = searchtype;
+                        if (Person::players[ally]->lastseentime < lastseentime) {
+                            Person::players[ally]->lastseen = lastseen;
+                            Person::players[ally]->lastseentime = lastseentime;
+                            Person::players[ally]->lastchecktime = lastchecktime;
+                        }
+                    }
+
+                    if (avoidcollided > .8 && !jumpkeydown && collided < .8) {
+                        if (!avoidsomething)
+                            targetyaw += 90 * (whichdirection * 2 - 1);
+                        else {
+                            XYZ leftpos, rightpos;
+                            float leftdist, rightdist;
+                            leftpos = coords + DoRotation(facing, 0, 90, 0);
+                            rightpos = coords - DoRotation(facing, 0, 90, 0);
+                            leftdist = distsq(&leftpos, &avoidwhere);
+                            rightdist = distsq(&rightpos, &avoidwhere);
+                            if (leftdist < rightdist)
+                                targetyaw += 90;
+                            else
+                                targetyaw -= 90;
+                        }
+                    }
+                }
+
+                leftkeydown = 0;
+                backkeydown = 0;
+                rightkeydown = 0;
+                crouchkeydown = 0;
+                attackkeydown = 0;
+            }
+            if (collided < 1 || animTarget != jumpupanim)
+                jumpkeydown = 0;
+            if (collided > .8 && jumppower >= 5)
+                jumpkeydown = 1;
+        }
+
+        //retreiving a weapon on the ground
+        if (aitype == getweapontype) {
+            aiupdatedelay -= multiplier;
+            lastchecktime -= multiplier;
+
+            if (aiupdatedelay < 0) {
+                aiupdatedelay = .2;
+
+                //ALLY IS WEPON
+                if (ally < 0) {
+                    int closest = -1;
+                    float closestdist = -1;
+                    for (unsigned k = 0; k < weapons.size(); k++)
+                        if (weapons[k].owner == -1) {
+                            float distance = distsq(&coords, &weapons[k].position);
+                            if (closestdist == -1 || distance < closestdist) {
+                                closestdist = distance;
+                                closest = k;
+                            }
+                            closest = k;
+                        }
+                    if (closest != -1)
+                        ally = closest;
+                    else
+                        ally = -1;
+                }
+
+                lastseentime = 12;
+
+                if (!Person::players[0]->dead && ((!Tutorial::active || cananger) && hostile))
+                    if (ally < 0 || weaponactive != -1 || lastchecktime <= 0) {
+                        aitype = attacktypecutoff;
+                        lastseentime = 1;
+                    }
+                if (!Person::players[0]->dead)
+                    if (ally >= 0) {
+                        if (weapons[ally].owner != -1 ||
+                                distsq(&coords, &weapons[ally].position) > 16) {
+                            aitype = attacktypecutoff;
+                            lastseentime = 1;
+                        }
+                        //TODO: factor these out as moveToward()
+                        targetyaw = roughDirectionTo(coords, weapons[ally].position);
+                        lookyaw = targetyaw;
+                        aiupdatedelay = .05;
+                        forwardkeydown = 1;
+
+
+                        if (avoidcollided > .8 && !jumpkeydown && collided < .8) {
+                            if (!avoidsomething)
+                                targetyaw += 90 * (whichdirection * 2 - 1);
+                            else {
+                                XYZ leftpos, rightpos;
+                                float leftdist, rightdist;
+                                leftpos = coords + DoRotation(facing, 0, 90, 0);
+                                rightpos = coords - DoRotation(facing, 0, 90, 0);
+                                leftdist = distsq(&leftpos, &avoidwhere);
+                                rightdist = distsq(&rightpos, &avoidwhere);
+                                if (leftdist < rightdist)
+                                    targetyaw += 90;
+                                else
+                                    targetyaw -= 90;
+                            }
+                        }
+                    }
+
+                leftkeydown = 0;
+                backkeydown = 0;
+                rightkeydown = 0;
+                attackkeydown = 0;
+                throwkeydown = 1;
+                crouchkeydown = 0;
+                if (animTarget != crouchremoveknifeanim &&
+                        animTarget != removeknifeanim)
+                    throwtogglekeydown = 0;
+                drawkeydown = 0;
+            }
+            if (collided < 1 || animTarget != jumpupanim)
+                jumpkeydown = 0;
+            if ((collided > .8 && jumppower >= 5))
+                jumpkeydown = 1;
+        }
+
+        if (aitype == attacktypecutoff) {
+            aiupdatedelay -= multiplier;
+            //dodge or reverse rabbit kicks, knife throws, flips
+            if (damage < damagetolerance * 2 / 3)
+                if ((Person::players[0]->animTarget == rabbitkickanim ||
+                        Person::players[0]->animTarget == knifethrowanim ||
+                        (Person::players[0]->isFlip() &&
+                         normaldotproduct(Person::players[0]->facing, Person::players[0]->coords - coords) < 0)) &&
+                        !Person::players[0]->skeleton.free &&
+                        (aiupdatedelay < .1)) {
+                    attackkeydown = 0;
+                    if (isIdle())
+                        crouchkeydown = 1;
+                    if (Person::players[0]->animTarget != rabbitkickanim && Person::players[0]->weaponactive != -1) {
+                        if (weapons[Person::players[0]->weaponids[0]].getType() == knife) {
+                            if (isIdle() || isCrouch() || isRun() || isFlip()) {
+                                if (abs(Random() % 2) == 0) {
+                                    setTargetAnimation(backhandspringanim);
+                                } else {
+                                    setTargetAnimation(rollanim);
+                                }
+                                targetyaw += 90 * (abs(Random() % 2) * 2 - 1);
+                                wentforweapon = 0;
+                            }
+                            if (animTarget == jumpupanim || animTarget == jumpdownanim) {
+                                setTargetAnimation(flipanim);
+                            }
+                        }
+                    }
+                    forwardkeydown = 0;
+                    aiupdatedelay = .02;
+                }
+            //get confused by flips
+            if (Person::players[0]->isFlip() &&
+                    !Person::players[0]->skeleton.free &&
+                    Person::players[0]->animTarget != walljumprightkickanim &&
+                    Person::players[0]->animTarget != walljumpleftkickanim) {
+                if (distsq(&Person::players[0]->coords, &coords) < 25)
+                    if ((1 - damage / damagetolerance) > .5)
+                        stunned = 1;
+            }
+            //go for weapon on the ground
+            if (wentforweapon < 3)
+                for (unsigned k = 0; k < weapons.size(); k++)
+                    if (creature != wolftype)
+                        if (num_weapons == 0 &&
+                            weapons[k].owner == -1 &&
+                            weapons[k].velocity.x == 0 &&
+                            weapons[k].velocity.z == 0 &&
+                            weapons[k].velocity.y == 0) {
+                            if (distsq(&coords, &weapons[k].position) < 16) {
+                                wentforweapon++;
+                                lastchecktime = 6;
+                                aitype = getweapontype;
+                                ally = -1;
+                            }
+                        }
+            //dodge/reverse walljump kicks
+            if (damage < damagetolerance / 2)
+                if (Animation::animations[animTarget].height != highheight)
+                    if (damage < damagetolerance * .5 &&
+                            ((Person::players[0]->animTarget == walljumprightkickanim ||
+                              Person::players[0]->animTarget == walljumpleftkickanim) &&
+                             ((aiupdatedelay < .15 &&
+                               difficulty == 2) ||
+                              (aiupdatedelay < .08 &&
+                               difficulty != 2)))) {
+                        crouchkeydown = 1;
+                    }
+            //walked off a ledge (?)
+            if (isRun() && !onground)
+                if (coords.y > terrain.getHeight(coords.x, coords.z) + 10) {
+                    XYZ test2 = coords + facing;
+                    test2.y += 5;
+                    XYZ test = coords + facing;
+                    test.y -= 10;
+                    j = Object::checkcollide(test2, test, laststanding);
+                    if (j == -1)
+                        j = Object::checkcollide(test2, test);
+                    if (j == -1) {
+                        velocity = 0;
+                        setTargetAnimation(getStop());
+                        targetyaw += 180;
+                        stunned = .5;
+                        aitype = pathfindtype;
+                        finalfinaltarget = waypoints[waypoint];
+                        finalpathfindpoint = -1;
+                        targetpathfindpoint = -1;
+                        lastpathfindpoint = -1;
+                        lastpathfindpoint2 = -1;
+                        lastpathfindpoint3 = -1;
+                        lastpathfindpoint4 = -1;
+                    } else
+                        laststanding = j;
+                }
+            //lose sight of player in the air (?)
+            if (Person::players[0]->coords.y > coords.y + 5 &&
+                    Animation::animations[Person::players[0]->animTarget].height != highheight &&
+                    !Person::players[0]->onterrain) {
+                aitype = pathfindtype;
+                finalfinaltarget = waypoints[waypoint];
+                finalpathfindpoint = -1;
+                targetpathfindpoint = -1;
+                lastpathfindpoint = -1;
+                lastpathfindpoint2 = -1;
+                lastpathfindpoint3 = -1;
+                lastpathfindpoint4 = -1;
+            }
+            //it's time to think (?)
+            if (aiupdatedelay < 0 &&
+                    !Animation::animations[animTarget].attack &&
+                    animTarget != staggerbackhighanim &&
+                    animTarget != staggerbackhardanim &&
+                    animTarget != backhandspringanim &&
+                    animTarget != dodgebackanim) {
+                //draw weapon
+                if (weaponactive == -1 && num_weapons > 0)
+                    drawkeydown = Random() % 2;
+                else
+                    drawkeydown = 0;
+                rabbitkickenabled = Random() % 2;
+                //chase player
+                XYZ rotatetarget = Person::players[0]->coords + Person::players[0]->velocity;
+                XYZ targetpoint = Person::players[0]->coords;
+                if (distsq(&Person::players[0]->coords, &coords) <
+                        distsq(&rotatetarget, &coords))
+                    targetpoint += Person::players[0]->velocity *
+                                   findDistance(&Person::players[0]->coords, &coords) / findLength(&velocity);
+                targetyaw = roughDirectionTo(coords, targetpoint);
+                lookyaw = targetyaw;
+                aiupdatedelay = .2 + fabs((float)(Random() % 100) / 1000);
+
+                if (distsq(&coords, &Person::players[0]->coords) > 5 && (Person::players[0]->weaponactive == -1 || weaponactive != -1))
+                    forwardkeydown = 1;
+                else if ((distsq(&coords, &Person::players[0]->coords) > 16 ||
+                          distsq(&coords, &Person::players[0]->coords) < 9) &&
+                         Person::players[0]->weaponactive != -1)
+                    forwardkeydown = 1;
+                else if (Random() % 6 == 0 || (creature == wolftype && Random() % 3 == 0))
+                    forwardkeydown = 1;
+                else
+                    forwardkeydown = 0;
+                //chill out around the corpse
+                if (Person::players[0]->dead) {
+                    forwardkeydown = 0;
+                    if (Random() % 10 == 0)
+                        forwardkeydown = 1;
+                    if (Random() % 100 == 0) {
+                        aitype = pathfindtype;
+                        finalfinaltarget = waypoints[waypoint];
+                        finalpathfindpoint = -1;
+                        targetpathfindpoint = -1;
+                        lastpathfindpoint = -1;
+                        lastpathfindpoint2 = -1;
+                        lastpathfindpoint3 = -1;
+                        lastpathfindpoint4 = -1;
+                    }
+                }
+                leftkeydown = 0;
+                backkeydown = 0;
+                rightkeydown = 0;
+                crouchkeydown = 0;
+                throwkeydown = 0;
+
+                if (avoidcollided > .8 && !jumpkeydown && collided < .8)
+                    targetyaw += 90 * (whichdirection * 2 - 1);
+                //attack!!!
+                if (Random() % 2 == 0 || weaponactive != -1 || creature == wolftype)
+                    attackkeydown = 1;
+                else
+                    attackkeydown = 0;
+                if (isRun() && Random() % 6 && distsq(&coords, &Person::players[0]->coords) > 7)
+                    attackkeydown = 0;
+
+                //TODO: wat
+                if (aitype != playercontrolled &&
+                        (isIdle() ||
+                         isCrouch() ||
+                         isRun())) {
+                    int target = -2;
+                    for (unsigned j = 0; j < Person::players.size(); j++)
+                        if (j != id && !Person::players[j]->skeleton.free &&
+                                Person::players[j]->hasvictim &&
+                                (Tutorial::active && reversaltrain ||
+                                 Random() % 2 == 0 && difficulty == 2 ||
+                                 Random() % 4 == 0 && difficulty == 1 ||
+                                 Random() % 8 == 0 && difficulty == 0 ||
+                                 Person::players[j]->lastattack2 == Person::players[j]->animTarget &&
+                                 Person::players[j]->lastattack3 == Person::players[j]->animTarget &&
+                                 (Random() % 2 == 0 || difficulty == 2) ||
+                                 (isIdle() || isRun()) &&
+                                 Person::players[j]->weaponactive != -1 ||
+                                 Person::players[j]->animTarget == swordslashanim &&
+                                 weaponactive != -1 ||
+                                 Person::players[j]->animTarget == staffhitanim ||
+                                 Person::players[j]->animTarget == staffspinhitanim))
+                            if (distsq(&Person::players[j]->coords, &Person::players[j]->victim->coords) < 4 &&
+                                    Person::players[j]->victim == Person::players[id] &&
+                                    (Person::players[j]->animTarget == sweepanim ||
+                                     Person::players[j]->animTarget == spinkickanim ||
+                                     Person::players[j]->animTarget == staffhitanim ||
+                                     Person::players[j]->animTarget == staffspinhitanim ||
+                                     Person::players[j]->animTarget == winduppunchanim ||
+                                     Person::players[j]->animTarget == upunchanim ||
+                                     Person::players[j]->animTarget == wolfslapanim ||
+                                     Person::players[j]->animTarget == knifeslashstartanim ||
+                                     Person::players[j]->animTarget == swordslashanim &&
+                                     (distsq(&Person::players[j]->coords, &coords) < 2 ||
+                                      weaponactive != -1))) {
+                                if (target >= 0) {
+                                    target = -1;
+                                } else {
+                                    target = j;
+                                }
+                            }
+                    if (target >= 0)
+                        Person::players[target]->Reverse();
+                }
+
+                if (collided < 1)
+                    jumpkeydown = 0;
+                if (collided > .8 && jumppower >= 5 ||
+                        distsq(&coords, &Person::players[0]->coords) > 400 &&
+                        onterrain &&
+                        creature == rabbittype)
+                    jumpkeydown = 1;
+                //TODO: why are we controlling the human?
+                if (normaldotproduct(facing, Person::players[0]->coords - coords) > 0)
+                    Person::players[0]->jumpkeydown = 0;
+                if (Person::players[0]->animTarget == jumpdownanim &&
+                        distsq(&Person::players[0]->coords, &coords) < 40)
+                    crouchkeydown = 1;
+                if (jumpkeydown)
+                    attackkeydown = 0;
+
+                if (Tutorial::active)
+                    if (!canattack)
+                        attackkeydown = 0;
+
+
+                XYZ facing = coords;
+                XYZ flatfacing = Person::players[0]->coords;
+                facing.y += jointPos(head).y * scale;
+                flatfacing.y += Person::players[0]->jointPos(head).y * Person::players[0]->scale;
+                if (occluded >= 2)
+                    if (-1 != Object::checkcollide(facing, flatfacing)) {
+                        if (!pause)
+                            lastseentime -= .2;
+                        if (lastseentime <= 0 &&
+                                (creature != wolftype ||
+                                 weaponstuck == -1)) {
+                            aitype = searchtype;
+                            lastchecktime = 12;
+                            lastseen = Person::players[0]->coords;
+                            lastseentime = 12;
+                        }
+                    } else
+                        lastseentime = 1;
+            }
+        }
+        if (Animation::animations[Person::players[0]->animTarget].height == highheight &&
+                (aitype == attacktypecutoff ||
+                 aitype == searchtype))
+            if (Person::players[0]->coords.y > terrain.getHeight(Person::players[0]->coords.x, Person::players[0]->coords.z) + 10) {
+                XYZ test = Person::players[0]->coords;
+                test.y -= 40;
+                if (-1 == Object::checkcollide(Person::players[0]->coords, test))
+                    stunned = 1;
+            }
+        //stunned
+        if (aitype == passivetype && !(numwaypoints > 1) ||
+                stunned > 0 ||
+                pause && damage > superpermanentdamage) {
+            if (pause)
+                lastseentime = 1;
+            targetyaw = yaw;
+            forwardkeydown = 0;
+            leftkeydown = 0;
+            backkeydown = 0;
+            rightkeydown = 0;
+            jumpkeydown = 0;
+            attackkeydown = 0;
+            crouchkeydown = 0;
+            throwkeydown = 0;
+        }
+
+
+        XYZ facing;
+        facing = 0;
+        facing.z = -1;
+
+        XYZ flatfacing = DoRotation(facing, 0, yaw + 180, 0);
+        facing = flatfacing;
+
+        if (aitype == attacktypecutoff) {
+            targetheadyaw = 180 - roughDirectionTo(coords, Person::players[0]->coords);
+            targetheadpitch = pitchTo(coords, Person::players[0]->coords);
+        } else if (howactive >= typesleeping) {
+            targetheadyaw = targetyaw;
+            targetheadpitch = 0;
+        } else {
+            if (interestdelay <= 0) {
+                interestdelay = .7 + (float)(abs(Random() % 100)) / 100;
+                headtarget = coords;
+                headtarget.x += (float)(abs(Random() % 200) - 100) / 100;
+                headtarget.z += (float)(abs(Random() % 200) - 100) / 100;
+                headtarget.y += (float)(abs(Random() % 200) - 100) / 300;
+                headtarget += facing * 1.5;
+            }
+            targetheadyaw = 180 - roughDirectionTo(coords, headtarget);
+            targetheadpitch = pitchTo(coords, headtarget);
+        }
+    }
+}