+
+void Person::doAI()
+{
+ if (!isPlayerControlled() && !Dialog::inDialog()) {
+ jumpclimb = 0;
+ //disable movement in editor
+ if (Game::editorenabled) {
+ stunned = 1;
+ }
+
+ pause = 0;
+ if (distsqflat(&victim->coords, &coords) < 30 &&
+ victim->coords.y > coords.y + 2 &&
+ !victim->onterrain) {
+ pause = 1;
+ }
+
+ if (isPlayerFriend() && aitype == pathfindtype) {
+ aitype = passivetype;
+ }
+
+ //pathfinding
+ if (aitype == pathfindtype) {
+ if (finalpathfindpoint == -1) {
+ float closestdistance;
+ float tempdist = 0.0f;
+ 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 = 0.0f;
+ 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 &&
+ !isPlayerFriend() &&
+ !victim->dead &&
+ distsq(&coords, &victim->coords) < 400 &&
+ occluded < 25) {
+ if (distsq(&coords, &victim->coords) < 12 &&
+ Animation::animations[victim->animTarget].height != lowheight &&
+ !Game::editorenabled &&
+ (victim->coords.y < coords.y + 5 || victim->onterrain)) {
+ aitype = attacktypecutoff;
+ }
+ if (distsq(&coords, &victim->coords) < 30 &&
+ Animation::animations[victim->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) && !isPlayerFriend() && 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) &&
+ !isPlayerFriend() &&
+ !victim->dead &&
+ distsq(&coords, &victim->coords) < 400 &&
+ occluded < 25) {
+ if (distsq(&coords, &victim->coords) < 12 &&
+ Animation::animations[victim->animTarget].height != lowheight && !Game::editorenabled) {
+ aitype = attacktypecutoff;
+ }
+ if (distsq(&coords, &victim->coords) < 30 &&
+ Animation::animations[victim->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) && !isPlayerFriend() && hostile)) {
+ for (int k = 0; k < numenvsounds; k++) {
+ if (distsq(&coords, &envsound[k]) < 2 * (envsoundvol[k] + envsoundvol[k] * (creature == rabbittype) * 3)) {
+ aitype = attacktypecutoff;
+ }
+ }
+ }
+
+ if (!victim->dead &&
+ !isPlayerFriend() &&
+ losupdatedelay < 0 &&
+ !Game::editorenabled &&
+ occluded < 2 &&
+ ((!Tutorial::active || cananger) && hostile)) {
+ losupdatedelay = .2;
+ if (distsq(&coords, &victim->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, &victim->coords) < 400) {
+ if (normaldotproduct(facing, victim->coords - coords) > 0) {
+ if ((Object::checkcollide(
+ DoRotation(jointPos(head), 0, yaw, 0) *
+ scale +
+ coords,
+ DoRotation(victim->jointPos(head), 0, victim->yaw, 0) *
+ victim->scale +
+ victim->coords) == -1) ||
+ (victim->animTarget == hanganim && normaldotproduct(victim->facing, coords - victim->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;
+ }
+ }
+ }
+ if (closest != -1) {
+ ally = closest;
+ } else {
+ ally = 0;
+ }
+ lastseen = victim->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;
+ }
+ }
+ }
+ if (closest != -1) {
+ ally = closest;
+ } else {
+ ally = -1;
+ }
+ }
+
+ lastseentime = 12;
+
+ if (!victim->dead && !isPlayerFriend() && ((!Tutorial::active || cananger) && hostile)) {
+ if (ally < 0 || hasWeapon() || lastchecktime <= 0) {
+ aitype = attacktypecutoff;
+ lastseentime = 1;
+ }
+ }
+ if (!victim->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 ((victim->animTarget == rabbitkickanim ||
+ victim->animTarget == knifethrowanim ||
+ (victim->isFlip() &&
+ normaldotproduct(victim->facing, victim->coords - coords) < 0)) &&
+ !victim->skeleton.free &&
+ (aiupdatedelay < .1)) {
+ attackkeydown = 0;
+ if (isIdle()) {
+ crouchkeydown = 1;
+ }
+ if (victim->animTarget != rabbitkickanim && victim->hasWeapon()) {
+ if (weapons[victim->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 (victim->isFlip() &&
+ !victim->skeleton.free &&
+ victim->animTarget != walljumprightkickanim &&
+ victim->animTarget != walljumpleftkickanim) {
+ if (distsq(&victim->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 &&
+ ((victim->animTarget == walljumprightkickanim ||
+ victim->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 (victim->coords.y > coords.y + 5 &&
+ Animation::animations[victim->animTarget].height != highheight &&
+ !victim->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 (!hasWeapon() && num_weapons > 0) {
+ drawkeydown = Random() % 2;
+ } else {
+ drawkeydown = 0;
+ }
+ rabbitkickenabled = Random() % 2;
+ //chase player
+ XYZ rotatetarget = victim->coords + victim->velocity;
+ XYZ targetpoint = victim->coords;
+ float vellength = findLength(&velocity);
+ if (vellength != 0 &&
+ distsq(&victim->coords, &coords) < distsq(&rotatetarget, &coords)) {
+ targetpoint += victim->velocity *
+ findDistance(&victim->coords, &coords) / vellength;
+ }
+ targetyaw = roughDirectionTo(coords, targetpoint);
+ lookyaw = targetyaw;
+ aiupdatedelay = .2 + fabs((float)(Random() % 100) / 1000);
+
+ if (distsq(&coords, &victim->coords) > 5 && (!victim->hasWeapon() || hasWeapon())) {
+ forwardkeydown = 1;
+ } else if ((distsq(&coords, &victim->coords) > 16 ||
+ distsq(&coords, &victim->coords) < 9) &&
+ victim->hasWeapon()) {
+ forwardkeydown = 1;
+ } else if (Random() % 6 == 0 || (creature == wolftype && Random() % 3 == 0)) {
+ forwardkeydown = 1;
+ } else {
+ forwardkeydown = 0;
+ }
+ //chill out around the corpse
+ if (victim->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 || hasWeapon() || creature == wolftype) {
+ attackkeydown = 1;
+ } else {
+ attackkeydown = 0;
+ }
+ if (isRun() && Random() % 6 && distsq(&coords, &victim->coords) > 7) {
+ attackkeydown = 0;
+ }
+
+ //TODO: wat
+ if (!isPlayerControlled() &&
+ (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]->hasWeapon() ||
+ Person::players[j]->animTarget == swordslashanim &&
+ hasWeapon() ||
+ 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 ||
+ hasWeapon()))) {
+ 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, &victim->coords) > 400 &&
+ onterrain &&
+ creature == rabbittype) {
+ jumpkeydown = 1;
+ }
+ //TODO: why are we controlling the human?
+ if (normaldotproduct(facing, victim->coords - coords) > 0) {
+ victim->jumpkeydown = 0;
+ }
+ if (victim->animTarget == jumpdownanim &&
+ distsq(&victim->coords, &coords) < 40) {
+ crouchkeydown = 1;
+ }
+ if (jumpkeydown) {
+ attackkeydown = 0;
+ }
+
+ if (Tutorial::active) {
+ if (!canattack) {
+ attackkeydown = 0;
+ }
+ }
+
+ XYZ facing = coords;
+ XYZ flatfacing = victim->coords;
+ facing.y += jointPos(head).y * scale;
+ flatfacing.y += victim->jointPos(head).y * victim->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 = victim->coords;
+ lastseentime = 12;
+ }
+ } else {
+ lastseentime = 1;
+ }
+ }
+ }
+ }
+ if (Animation::animations[victim->animTarget].height == highheight &&
+ (aitype == attacktypecutoff ||
+ aitype == searchtype)) {
+ if (victim->coords.y > terrain.getHeight(victim->coords.x, victim->coords.z) + 10) {
+ XYZ test = victim->coords;
+ test.y -= 40;
+ if (-1 == Object::checkcollide(victim->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;
+ }
+
+ if (stunned < 1 && isPlayerFriend()) {
+ // friends help player with its enemies
+ if (Game::musictype == stream_fighttheme && hasvictim) {
+ aitype = attacktypecutoff;
+ } else {
+ aitype = pathfindtype;
+ }
+
+ // friends follow player
+ if (!Person::players[0]->dead &&
+ aitype == pathfindtype &&
+ distsq(&coords, &Person::players[0]->coords) > 5) {
+ XYZ rotatetarget = Person::players[0]->coords + Person::players[0]->velocity;
+ XYZ targetpoint = Person::players[0]->coords;
+ velocity = (targetpoint - coords);
+ velocity.y += 2;
+ float vellength = findLength(&velocity);
+ if (vellength != 0 &&
+ distsq(&Person::players[0]->coords, &coords) < distsq(&rotatetarget, &coords)) {
+ targetpoint += Person::players[0]->velocity *
+ findDistance(&Person::players[0]->coords, &coords) / vellength;
+ }
+ targetyaw = roughDirectionTo(coords, targetpoint);
+ lookyaw = targetyaw;
+ forwardkeydown = 1;
+ }
+ }
+
+ 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 (isPlayerFriend()) {
+ targetheadyaw = 180 - roughDirectionTo(coords, Person::players[0]->coords);
+ targetheadpitch = pitchTo(coords, Person::players[0]->coords);
+ } 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);
+ }
+ }
+}
+
+bool Person::catchKnife()
+{
+ return
+ ((PersonType::types[creature].knifeCatchingType == 0) && (Random() % 2 != 0) && (!hasWeapon()) && (aitype == attacktypecutoff)) ||
+ ((PersonType::types[creature].knifeCatchingType == 1) && (Random() % 3 != 0) && (!hasWeapon()) && (isIdle() || isRun() || animTarget == walkanim));
+}