1 #ifndef H_ENEMY 2 #define H_ENEMY 3 4 #include "character.h" 5 #include "objects.h" 6 7 #define STALK_BOX (1024 * 3) 8 #define ESCAPE_BOX (1024 * 5) 9 #define ATTACK_BOX STALK_BOX 10 11 #define MAX_SHOT_DIST (64 * 1024) 12 13 struct Enemy : Character { 14 15 struct Path { 16 int16 index; 17 int16 count; 18 uint16 *boxes; 19 TR::Level *level; 20 PathEnemy::Path21 Path(TR::Level *level, uint16 *boxes, int count) : index(0), count(count), boxes(new uint16[count]), level(level) { 22 memcpy(this->boxes, boxes, count * sizeof(boxes[0])); 23 } 24 ~PathEnemy::Path25 ~Path() { 26 delete[] boxes; 27 } 28 getNextPointEnemy::Path29 bool getNextPoint(TR::Level *level, vec3 &point) { 30 if (index >= count - 1) 31 return false; 32 33 TR::Box &a = level->boxes[boxes[index++]]; 34 TR::Box &b = level->boxes[boxes[index]]; 35 36 int minX = max(a.minX, b.minX); 37 int minZ = max(a.minZ, b.minZ); 38 int maxX = min(a.maxX, b.maxX); 39 int maxZ = min(a.maxZ, b.maxZ); 40 41 point.x = float(minX + 512) + randf() * (maxX - minX - 1024); 42 point.y = float((a.floor + b.floor) / 2); 43 point.z = float(minZ + 512) + randf() * (maxZ - minZ - 1024); 44 45 return true; 46 } 47 }; 48 49 enum AI { 50 AI_FIXED, AI_RANDOM 51 } ai; 52 53 enum Mood { 54 MOOD_SLEEP, MOOD_STALK, MOOD_ATTACK, MOOD_ESCAPE 55 } mood; 56 57 bool wound; 58 int nextState; 59 60 uint16 targetBox; 61 vec3 waypoint; 62 63 float thinkTime; 64 float length; // dist from center to head (jaws) 65 float aggression; 66 int radius; 67 int hitSound; 68 69 Character *target; 70 Path *path; 71 72 float targetDist; 73 float targetAngle; 74 bool targetDead; 75 bool targetInView; // target in enemy view zone 76 bool targetFromView; // enemy in target view zone 77 bool targetCanAttack; 78 EnemyEnemy79 Enemy(IGame *game, int entity, float health, int radius, float length, float aggression) : Character(game, entity, health), ai(AI_RANDOM), mood(MOOD_SLEEP), wound(false), nextState(0), targetBox(TR::NO_BOX), thinkTime(1.0f / 30.0f), length(length), aggression(aggression), radius(radius), hitSound(-1), target(NULL), path(NULL) { 80 targetDist = +INF; 81 targetInView = targetFromView = targetCanAttack = false; 82 waypoint = pos; 83 } 84 ~EnemyEnemy85 virtual ~Enemy() { 86 delete path; 87 } 88 getSaveDataEnemy89 virtual bool getSaveData(SaveEntity &data) { 90 Character::getSaveData(data); 91 data.extraSize = sizeof(data.extra.enemy); 92 data.extra.enemy.health = health; 93 data.extra.enemy.spec.mood = mood; 94 data.extra.enemy.targetBox = targetBox; 95 return true; 96 } 97 setSaveDataEnemy98 virtual void setSaveData(const SaveEntity &data) { 99 Character::setSaveData(data); 100 health = data.extra.enemy.health; 101 mood = Mood(data.extra.enemy.spec.mood); 102 targetBox = data.extra.enemy.targetBox; 103 updateZone(); 104 } 105 activateEnemy106 virtual bool activate() { 107 return health > 0.0f && Character::activate(); 108 } 109 updateVelocityEnemy110 virtual void updateVelocity() { 111 if (stand == STAND_AIR && (!flying || health <= 0.0f)) 112 applyGravity(velocity.y); 113 else 114 velocity = getDir() * animation.getSpeed(); 115 116 if (health <= 0.0f) 117 velocity.x = velocity.z = 0.0f; 118 } 119 checkPointEnemy120 bool checkPoint(int x, int z) { 121 TR::Box &a = level->boxes[box]; 122 if (a.contains(x, z)) 123 return true; 124 125 bool big = getEntity().isBigEnemy(); 126 TR::Overlap *o = &level->overlaps[a.overlap.index]; 127 do { 128 TR::Box &b = level->boxes[o->boxIndex]; 129 if (!b.contains(x, z)) 130 continue; 131 if (big && b.overlap.blockable) 132 continue; 133 if (getZones()[o->boxIndex] == zone) { 134 int d = a.floor - b.floor; 135 if (d <= stepHeight && d >= dropHeight) 136 return true; 137 } 138 } while (!(o++)->end); 139 140 return false; 141 } 142 clipByBoxEnemy143 void clipByBox(vec3 &pos) { 144 int px = int(pos.x); 145 int pz = int(pos.z); 146 int nx = px; 147 int nz = pz; 148 149 TR::Box &a = level->boxes[box]; 150 151 if (!checkPoint(px - radius, pz)) nx = a.minX + radius; 152 if (!checkPoint(px + radius, pz)) nx = a.maxX - radius; 153 if (!checkPoint(px, pz - radius)) nz = a.minZ + radius; 154 if (!checkPoint(px, pz + radius)) nz = a.maxZ - radius; 155 156 if (px != nx) pos.x = float(nx); 157 if (pz != nz) pos.z = float(nz); 158 } 159 collideEnemiesEnemy160 void collideEnemies() { 161 if (getEntity().isBigEnemy()) 162 return; 163 164 Controller *c = Controller::first; 165 while (c) { 166 if (c != this && c->getEntity().isEnemy()) { 167 Enemy *enemy = (Enemy*)c; 168 if (enemy->health > 0.0f) { 169 vec3 dir = vec3(enemy->pos.x - pos.x, 0.0f, enemy->pos.z - pos.z); 170 float D = dir.length2(); 171 float R = float((enemy->radius + radius) / 2); 172 if (D < R * R) { 173 D = sqrtf(D); 174 pos -= dir.normal() * (R - D); 175 } 176 } 177 } 178 c = c->next; 179 } 180 } 181 updatePositionEnemy182 virtual void updatePosition() { 183 if (!flags.active) return; 184 185 vec3 p = pos; 186 pos += velocity * (30.0f * Core::deltaTime); 187 188 collideEnemies(); 189 190 clipByBox(pos); 191 192 TR::Level::FloorInfo info; 193 getFloorInfo(getRoomIndex(), pos, info); 194 if (stand == STAND_AIR && !flying && info.floor < pos.y) { 195 stand = STAND_GROUND; 196 pos.y = info.floor; 197 } 198 199 if (info.boxIndex != 0xFFFF && zone == getZones()[info.boxIndex] && !level->boxes[info.boxIndex].overlap.block) { 200 switch (stand) { 201 case STAND_GROUND : { 202 float fallSpeed = 2048.0f * Core::deltaTime; 203 decrease(info.floor - pos.y, pos.y, fallSpeed); 204 break; 205 } 206 case STAND_AIR : 207 pos.y = clamp(pos.y, info.ceiling, info.floor); 208 break; 209 default : ; 210 } 211 } else 212 pos = p; 213 214 updateRoom(); 215 } 216 setOverridesEnemy217 void setOverrides(bool active, int chest, int head) { 218 if (active && head > -1) { 219 animation.overrides[head] = animation.getJointRot(head); 220 animation.overrideMask |= (1 << head); 221 } else 222 animation.overrideMask &= ~(1 << head); 223 224 if (active && chest > -1) { 225 animation.overrides[chest] = animation.getJointRot(chest); 226 animation.overrideMask |= (1 << chest); 227 } else 228 animation.overrideMask &= ~(1 << chest); 229 } 230 targetIsVisibleEnemy231 bool targetIsVisible(float maxDist) { 232 if (targetInView && targetDist < maxDist && target->health > 0.0f) { 233 TR::Location from, to; 234 from.room = getRoomIndex(); 235 from.pos = pos; 236 to.pos = target->pos; 237 238 // vertical offset to ~gun/head height 239 from.pos.y -= 768.0f; 240 if (target->stand != STAND_UNDERWATER && target->stand != STAND_ONWATER) 241 to.pos.y -= 768.0f; 242 243 return trace(from, to); 244 } 245 return false; 246 } 247 lookAtEnemy248 virtual void lookAt(Controller *target) { 249 Character::lookAt(targetInView ? target : NULL); 250 } 251 turnEnemy252 void turn(bool tilt, float w) { 253 float speed = animation.getSpeed(); 254 255 if (!target || speed == 0.0f || w == 0.0f) { 256 angle.z = lerp(angle.z, 0.0f, 4.0f * Core::deltaTime); 257 return; 258 } 259 260 vec3 d = waypoint - pos; 261 float a = clampAngle(normalizeAngle(PIH - d.angleY() - angle.y)); 262 263 w /= 30.0f; 264 265 float minDist = speed * PIH / w; 266 267 if ( (a > PIH || a < -PIH) && (SQR(d.x) + SQR(d.z) < SQR(minDist)) ) 268 w *= 0.5f; 269 270 a = clamp(a, -w, w); 271 272 angle.y += a * 30.0f * Core::deltaTime; 273 angle.z = lerp(angle.z, tilt ? a * 2.0f : 0.0f, 4.0f * Core::deltaTime); 274 } 275 liftEnemy276 int lift(float delta, float speed) { 277 speed *= Core::deltaTime; 278 decrease(delta, pos.y, speed); 279 if (speed != 0.0f) { 280 return speed < 0 ? FORTH : BACK; 281 } 282 return 0; 283 } 284 285 virtual void hit(float damage, Controller *enemy = NULL, TR::HitType hitType = TR::HIT_DEFAULT) { 286 if (hitSound > -1 && health > 0.0f) 287 game->playSound(hitSound, pos, Sound::PAN); 288 Character::hit(damage, enemy, hitType); 289 wound = true; 290 }; 291 biteEnemy292 void bite(int joint, const vec3 &offset, float damage) { 293 ASSERT(target); 294 target->hit(damage, this); 295 if (joint >= 0) 296 game->addEntity(TR::Entity::BLOOD, target->getRoomIndex(), getJoint(joint) * offset); 297 } 298 getMoodFixedEnemy299 Mood getMoodFixed() { 300 bool inZone = zone == target->zone; 301 302 if (mood == MOOD_SLEEP || mood == MOOD_STALK) 303 return inZone ? MOOD_ATTACK : (wound ? MOOD_ESCAPE : mood); 304 305 if (mood == MOOD_ATTACK) 306 return inZone ? mood : MOOD_SLEEP; 307 308 return inZone ? MOOD_ATTACK : mood; 309 } 310 getMoodRandomEnemy311 Mood getMoodRandom() { 312 bool inZone = zone == target->zone; 313 bool brave = rand() < (mood != MOOD_ESCAPE ? 0x7800 : 0x0100) && inZone; 314 315 if (mood == MOOD_SLEEP || mood == MOOD_STALK) { 316 if (wound && !brave) 317 return MOOD_ESCAPE; 318 if (inZone) { 319 int dx = abs(int(pos.x - target->pos.x)); 320 int dz = abs(int(pos.z - target->pos.z)); 321 return ((dx <= ATTACK_BOX && dz <= ATTACK_BOX) || (mood == MOOD_STALK && targetBox == TR::NO_BOX)) ? MOOD_ATTACK : MOOD_STALK; 322 } 323 return mood; 324 } 325 326 if (mood == MOOD_ATTACK) 327 return (wound && !brave) ? MOOD_ESCAPE : (!inZone ? MOOD_SLEEP : mood); 328 329 return brave ? MOOD_STALK : mood; 330 } 331 thinkEnemy332 bool think(bool fixedLogic) { 333 thinkTime += Core::deltaTime; 334 if (thinkTime < 1.0f / 30.0f) 335 return false; 336 thinkTime -= 1.0f / 30.0f; 337 338 int zoneOld = zone; 339 updateZone(); 340 341 target = (Character*)game->getLara(pos); 342 343 vec3 targetVec = target->pos - pos - getDir() * length; 344 targetDist = targetVec.length(); 345 targetAngle = clampAngle(atan2f(targetVec.x, targetVec.z) - angle.y); 346 targetDead = target->health <= 0; 347 targetInView = targetVec.dot(getDir()) > 0; 348 targetFromView = targetVec.dot(target->getDir()) < 0; 349 targetCanAttack = targetInView && fabsf(targetVec.y) <= 256.0f; 350 351 int targetBoxOld = targetBox; 352 353 bool inZone = zone == target->zone; 354 355 if (target->health <= 0.0f || !inZone) 356 targetBox = TR::NO_BOX; 357 358 // update mood 359 if (mood != MOOD_ATTACK && targetBox != TR::NO_BOX && !checkBox(targetBox)) { 360 if (!inZone) 361 mood = MOOD_SLEEP; 362 targetBox = TR::NO_BOX; 363 } 364 365 mood = target->health <= 0 ? MOOD_SLEEP : (ai == AI_FIXED ? getMoodFixed() : getMoodRandom()); 366 367 // set behavior and target 368 int box; 369 370 switch (mood) { 371 case MOOD_SLEEP : 372 if (targetBox == TR::NO_BOX && checkBox(box = getRandomZoneBox()) && isStalkBox(box)) { 373 mood = MOOD_STALK; 374 gotoBox(box); 375 } 376 break; 377 case MOOD_STALK : 378 if ((targetBox == TR::NO_BOX || !isStalkBox(targetBox)) && checkBox(box = getRandomZoneBox())) { 379 if (isStalkBox(box)) 380 gotoBox(box); 381 else 382 if (targetBox == TR::NO_BOX) { 383 if (!inZone) 384 mood = MOOD_SLEEP; 385 gotoBox(box); 386 } 387 } 388 break; 389 case MOOD_ATTACK : 390 if (randf() > aggression) 391 break; 392 targetBox = TR::NO_BOX; 393 break; 394 case MOOD_ESCAPE : 395 if (targetBox == TR::NO_BOX && checkBox(box = getRandomZoneBox())) { 396 if (isEscapeBox(box)) 397 gotoBox(box); 398 else 399 if (inZone && isStalkBox(box)) { 400 mood = MOOD_STALK; 401 gotoBox(box); 402 } 403 } 404 break; 405 } 406 407 if (targetBox == TR::NO_BOX) 408 gotoBox(target->box); 409 410 if (path && this->box != path->boxes[path->index - 1] && this->box != path->boxes[path->index]) 411 targetBoxOld = TR::NO_BOX; 412 413 if (zoneOld != zone) 414 targetBoxOld = TR::NO_BOX; 415 416 if (targetBoxOld != targetBox) { 417 if (findPath(stepHeight, dropHeight, getEntity().isBigEnemy())) 418 nextWaypoint(); 419 else 420 targetBox = TR::NO_BOX; 421 } 422 423 if (targetBox != TR::NO_BOX && path) { 424 vec3 d = pos - waypoint; 425 426 if (fabsf(d.x) < 512 && fabsf(d.y) < 512 && fabsf(d.z) < 512) 427 nextWaypoint(); 428 } 429 430 return true; 431 } 432 nextWaypointEnemy433 void nextWaypoint() { 434 if (!path->getNextPoint(level, waypoint)) 435 waypoint = target->pos; 436 } 437 getRandomZoneBoxEnemy438 uint16 getRandomZoneBox() { 439 return game->getRandomBox(zone, getZones()); 440 } 441 gotoBoxEnemy442 void gotoBox(int box) { 443 targetBox = box; 444 } 445 checkBoxEnemy446 bool checkBox(int box) { 447 if (zone != getZones()[box]) 448 return false; 449 450 TR::Box &b = game->getLevel()->boxes[box]; 451 uint16 type = getEntity().type; 452 453 if (b.overlap.block) 454 return false; 455 456 if (type == TR::Entity::ENEMY_REX || type == TR::Entity::ENEMY_MUTANT_1 || type == TR::Entity::ENEMY_CENTAUR) { 457 if (b.overlap.blockable) 458 return false; 459 } else 460 if (b.overlap.block) 461 return false; 462 463 return int(pos.x) < int(b.minX) || int(pos.x) > int(b.maxX) || int(pos.z) < int(b.minZ) || int(pos.z) > int(b.maxZ); 464 } 465 isStalkBoxEnemy466 bool isStalkBox(int box) { 467 TR::Box &b = game->getLevel()->boxes[box]; 468 469 int x = (b.minX + b.maxX) / 2 - int(target->pos.x); 470 if (abs(x) > STALK_BOX) return false; 471 472 int z = (b.minZ + b.maxZ) / 2 - int(target->pos.z); 473 if (abs(z) > STALK_BOX) return false; 474 475 int target_quadrant = angleQuadrant(target->angle.y, 0.0); 476 int box_quadrant = z > 0 ? (x > 0 ? 2 : 1) : (x > 0 ? 3 : 0); 477 478 if (target_quadrant == box_quadrant) return false; 479 480 int controller_quadrant = pos.z > target->pos.z ? (pos.x > target->pos.x ? 2 : 1) : (pos.x > target->pos.x ? 3 : 0); 481 482 if (target_quadrant == controller_quadrant && abs(target_quadrant - box_quadrant) == 2) return false; 483 484 return true; 485 } 486 isEscapeBoxEnemy487 bool isEscapeBox(int box) { 488 TR::Box &b = game->getLevel()->boxes[box]; 489 490 int x = (b.minX + b.maxX) / 2 - int(target->pos.x); 491 if (abs(x) < ESCAPE_BOX) return false; 492 493 int z = (b.minZ + b.maxZ) / 2 - int(target->pos.z); 494 if (abs(z) < ESCAPE_BOX) return false; 495 496 return !((pos.x > target->pos.x) ^ (x > 0)) || !((pos.z > target->pos.z) ^ (z > 0)); 497 } 498 findPathEnemy499 bool findPath(int ascend, int descend, bool big) { 500 delete path; 501 path = NULL; 502 503 uint16 *boxes; 504 uint16 count = game->findPath(ascend, descend, big, box, targetBox, getZones(), &boxes); 505 if (count) { 506 path = new Path(level, boxes, count); 507 return true; 508 } 509 510 return false; 511 } 512 shotEnemy513 void shot(TR::Entity::Type type, int joint, const vec3 &offset, float rx, float ry) { 514 vec3 from = getJoint(joint) * offset; 515 vec3 to = target->getBoundingBox().center(); 516 517 Bullet *bullet = (Bullet*)game->addEntity(type, getRoomIndex(), from); 518 if (bullet) { 519 vec3 dir = to - from; 520 vec3 ang = vec3(-atan2f(dir.y, sqrtf(dir.x * dir.x + dir.z * dir.z)), atan2f(dir.x, dir.z), 0.0f); 521 ang += vec3(rx, ry, 0.0f); 522 bullet->setAngle(ang); 523 } 524 } 525 shotEnemy526 void shot(TR::Entity::Type type, int joint, const vec3 &offset) { 527 shot(type, joint, offset, (randf() * 2.0f - 1.0f) * (1.5f * DEG2RAD), (randf() * 2.0f - 1.0f) * (1.5f * DEG2RAD)); 528 } 529 isVisibleEnemy530 bool isVisible() { 531 for (int i = 0; i < 2; i++) { 532 ICamera *camera = game->getCamera(i); 533 if (!camera) continue; 534 535 TR::Location eye = camera->eye; 536 TR::Location loc; 537 loc.room = getRoomIndex(); 538 loc.box = box; 539 loc.pos = pos; 540 loc.pos.y -= 1024; 541 542 if (trace(eye, loc)) 543 return true; 544 } 545 return false; 546 } 547 }; 548 549 550 #define WOLF_TURN_FAST (DEG2RAD * 150) 551 #define WOLF_TURN_SLOW (DEG2RAD * 60) 552 #define WOLF_DIST_STALK STALK_BOX 553 #define WOLF_DIST_BITE 345 554 #define WOLF_DIST_ATTACK (1024 + 512) 555 556 struct Wolf : Enemy { 557 558 enum { 559 HIT_MASK = 0x774F, // body, head, front legs 560 }; 561 562 enum { 563 ANIM_DEATH = 20, 564 ANIM_DEATH_RUN = 21, 565 ANIM_DEATH_JUMP = 22, 566 }; 567 568 enum { 569 STATE_NONE , 570 STATE_STOP , 571 STATE_WALK , 572 STATE_RUN , 573 STATE_JUMP , // unused 574 STATE_STALK , 575 STATE_ATTACK , 576 STATE_HOWL , 577 STATE_SLEEP , 578 STATE_GROWL , 579 STATE_TURN , // unused 580 STATE_DEATH , 581 STATE_BITE , 582 }; 583 WolfWolf584 Wolf(IGame *game, int entity) : Enemy(game, entity, 6, 341, 375.0f, 0.25f) { 585 dropHeight = -1024; 586 jointChest = 2; 587 jointHead = 3; 588 hitSound = TR::SND_HIT_WOLF; 589 nextState = STATE_NONE; 590 animation.time = animation.timeMax; 591 updateAnimation(false); 592 } 593 getStateGroundWolf594 virtual int getStateGround() { 595 if (!think(false)) 596 return state; 597 598 if (nextState == state) 599 nextState = STATE_NONE; 600 601 switch (state) { 602 case STATE_SLEEP : 603 if (mood == MOOD_ESCAPE || target->zone == zone) 604 nextState = STATE_GROWL; 605 else if (rand() < 32) 606 nextState = STATE_WALK; 607 else 608 break; 609 return STATE_STOP; 610 case STATE_STOP : return nextState != STATE_NONE ? nextState : STATE_WALK; 611 case STATE_WALK : 612 if (mood != MOOD_SLEEP) { 613 nextState = STATE_NONE; 614 return STATE_STALK; 615 } 616 if (rand() < 32) { 617 nextState = STATE_SLEEP; 618 return STATE_STOP; 619 } 620 break; 621 case STATE_GROWL : 622 if (nextState != STATE_NONE) return nextState; 623 if (mood == MOOD_ESCAPE) return STATE_RUN; 624 if (targetDist < WOLF_DIST_BITE && targetCanAttack) return STATE_BITE; 625 if (mood == MOOD_STALK) return STATE_STALK; 626 if (mood == MOOD_SLEEP) return STATE_STOP; 627 return STATE_RUN; 628 case STATE_STALK : 629 if (mood == MOOD_ESCAPE) return STATE_RUN; 630 if (targetDist < WOLF_DIST_BITE && targetCanAttack) return STATE_BITE; 631 if (targetDist > WOLF_DIST_STALK) return STATE_RUN; 632 if (mood == MOOD_ATTACK) { 633 if (!targetInView || targetFromView || targetDist > WOLF_DIST_ATTACK) 634 return STATE_RUN; 635 } 636 if (rand() < 384) { 637 nextState = STATE_HOWL; 638 return STATE_GROWL; 639 } 640 if (mood == MOOD_SLEEP) return STATE_GROWL; 641 break; 642 case STATE_RUN : 643 if (targetDist < WOLF_DIST_ATTACK && targetInView) { 644 if (targetDist < WOLF_DIST_ATTACK * 0.5f && targetFromView) { 645 nextState = STATE_NONE; 646 return STATE_ATTACK; 647 } 648 nextState = STATE_STALK; 649 return STATE_GROWL; 650 } 651 if (mood == MOOD_STALK && targetDist < WOLF_DIST_STALK) { 652 nextState = STATE_STALK; 653 return STATE_GROWL; 654 } 655 if (mood == MOOD_SLEEP) return STATE_GROWL; 656 break; 657 case STATE_ATTACK : 658 case STATE_BITE : 659 if (nextState == STATE_NONE && targetInView && (collide(target) & HIT_MASK)) { 660 bite(6, vec3(0.0f, -14.0f, 174.0f), state == STATE_ATTACK ? 50.0f : 100.0f); 661 nextState = state == STATE_ATTACK ? STATE_RUN : STATE_GROWL; 662 } 663 return state == STATE_ATTACK ? STATE_RUN : state; 664 } 665 666 return state; 667 } 668 getStateDeathWolf669 virtual int getStateDeath() { 670 switch (state) { 671 case STATE_DEATH : return state; 672 case STATE_RUN : return animation.setAnim(ANIM_DEATH_RUN); 673 case STATE_JUMP : return animation.setAnim(ANIM_DEATH_JUMP); 674 default : return animation.setAnim(ANIM_DEATH); 675 } 676 } 677 updatePositionWolf678 virtual void updatePosition() { 679 turn(state == STATE_RUN || state == STATE_WALK || state == STATE_STALK, state == STATE_RUN ? WOLF_TURN_FAST : WOLF_TURN_SLOW); 680 681 if (state == STATE_DEATH) { 682 animation.overrideMask = 0; 683 return; 684 } 685 686 Enemy::updatePosition(); 687 setOverrides(state != STATE_DEATH, jointChest, jointHead); 688 lookAt(target); 689 } 690 }; 691 692 693 #define LION_DIST_ATTACK 1024 694 #define LION_TURN_FAST (DEG2RAD * 150) 695 #define LION_TURN_SLOW (DEG2RAD * 60) 696 697 struct Lion : Enemy { 698 699 enum { 700 HIT_MASK = 0x380066, 701 }; 702 703 enum { 704 ANIM_DEATH_LION = 7, 705 ANIM_DEATH_PUMA = 4, 706 }; 707 708 enum { 709 STATE_NONE , 710 STATE_STOP , 711 STATE_WALK , 712 STATE_RUN , 713 STATE_ATTACK , 714 STATE_DEATH , 715 STATE_ROAR , 716 STATE_BITE , 717 }; 718 LionLion719 Lion(IGame *game, int entity) : Enemy(game, entity, 6, 341, 400.0f, 0.25f) { 720 dropHeight = -1024; 721 jointChest = 19; 722 jointHead = 20; 723 switch (getEntity().type) { 724 case TR::Entity::ENEMY_LION_MALE : 725 hitSound = TR::SND_HIT_LION; 726 health = 30.0f; 727 aggression = 1.0f; 728 break; 729 case TR::Entity::ENEMY_LION_FEMALE : 730 hitSound = TR::SND_HIT_LION; 731 health = 25.0f; 732 break; 733 case TR::Entity::ENEMY_PUMA : 734 health = 45.0f; 735 break; 736 default : ; 737 } 738 } 739 getStateGroundLion740 virtual int getStateGround() { 741 if (!think(true)) 742 return state; 743 744 if (nextState == state) 745 nextState = STATE_NONE; 746 747 switch (state) { 748 case STATE_STOP : 749 if (nextState != STATE_NONE) 750 return nextState; 751 if (mood == MOOD_SLEEP) 752 return STATE_WALK; 753 if (targetInView && (collide(target) & HIT_MASK)) 754 return STATE_BITE; 755 if (targetInView && targetDist < LION_DIST_ATTACK) 756 return STATE_ATTACK; 757 return STATE_RUN; 758 case STATE_WALK : 759 if (mood != MOOD_SLEEP) 760 return STATE_STOP; 761 if (randf() < 0.0004f) { 762 nextState = STATE_ROAR; 763 return STATE_STOP; 764 } 765 break; 766 case STATE_RUN : 767 if ((mood == MOOD_SLEEP) || 768 (targetInView && targetDist < LION_DIST_ATTACK) || 769 (targetInView && (collide(target) & HIT_MASK))) 770 return STATE_STOP; 771 if (mood == MOOD_ESCAPE && randf() < 0.0004f) { 772 nextState = STATE_ROAR; 773 return STATE_STOP; 774 } 775 break; 776 case STATE_ATTACK : 777 case STATE_BITE : 778 if (nextState == STATE_NONE && (collide(target) & HIT_MASK)) { 779 bite(21, vec3(-2.0f, -10.0f, 132.0f), state == STATE_ATTACK ? 150.0f : 250.0f); 780 nextState = STATE_STOP; 781 } 782 } 783 784 return state; 785 } 786 getStateDeathLion787 virtual int getStateDeath() { 788 if (state == STATE_DEATH) 789 return state; 790 int deathAnim = (getEntity().type == TR::Entity::ENEMY_PUMA) ? ANIM_DEATH_PUMA : ANIM_DEATH_LION; 791 return animation.setAnim(deathAnim + rand() % 2); 792 } 793 updatePositionLion794 virtual void updatePosition() { 795 turn(state == STATE_RUN || state == STATE_WALK || state == STATE_ROAR, state == STATE_RUN ? LION_TURN_FAST : LION_TURN_SLOW); 796 797 if (state == STATE_DEATH) { 798 animation.overrideMask = 0; 799 return; 800 } 801 802 Enemy::updatePosition(); 803 setOverrides(true, jointChest, jointHead); 804 lookAt(target); 805 } 806 }; 807 808 809 #define GORILLA_DIST_ATTACK 430 810 #define GORILLA_DIST_AGGRESSION 2048 811 #define GORILLA_TURN_FAST (DEG2RAD * 150) 812 813 struct Gorilla : Enemy { 814 815 enum { 816 HIT_MASK = 0x00FF00, 817 }; 818 819 enum { 820 ANIM_DEATH = 7, 821 ANIM_CLIMB = 19, 822 }; 823 824 enum { 825 STATE_NONE , 826 STATE_STOP , 827 STATE_UNUSED , 828 STATE_RUN , 829 STATE_ATTACK , 830 STATE_DEATH , 831 STATE_IDLE1 , 832 STATE_IDLE2 , 833 STATE_LEFT , 834 STATE_RIGHT , 835 STATE_JUMP , 836 STATE_CLIMB , 837 }; 838 839 enum { 840 FLAG_ATTACK = 1, 841 FLAG_LEFT = 2, 842 FLAG_RIGHT = 4, 843 }; 844 GorillaGorilla845 Gorilla(IGame *game, int entity) : Enemy(game, entity, 22, 341, 250.0f, 1.0f) { 846 dropHeight = -1024; 847 stepHeight = 1024; 848 jointChest = -1;//7; 849 jointHead = 14; 850 flags.unused = 0; 851 } 852 getStateGroundGorilla853 virtual int getStateGround() { 854 if (!think(true)) 855 return state; 856 857 if (nextState == state) 858 nextState = STATE_NONE; 859 860 if (targetDist < GORILLA_DIST_AGGRESSION) 861 flags.unused |= FLAG_ATTACK; 862 863 switch (state) { 864 case STATE_STOP : 865 if (nextState != STATE_NONE) 866 return nextState; 867 if (targetCanAttack && targetDist < GORILLA_DIST_ATTACK) 868 return STATE_ATTACK; 869 if (!(flags.unused & FLAG_ATTACK) && zone == target->zone && targetInView) { 870 int r = rand() % 512; 871 if (r < 120) return STATE_JUMP; 872 if (r < 240) return STATE_IDLE1; 873 if (r < 360) return STATE_IDLE2; 874 if (r < 480) return STATE_RUN; 875 return (r % 2) ? STATE_LEFT : STATE_RIGHT; 876 } 877 return STATE_RUN; 878 case STATE_RUN : 879 if (!flags.unused && targetInView) 880 return STATE_STOP; 881 if (targetInView && (collide(target) & HIT_MASK)) { 882 nextState = STATE_ATTACK; 883 return STATE_STOP; 884 } 885 if (mood != MOOD_ESCAPE) { 886 int r = rand(); 887 if (r < 160) 888 nextState = STATE_JUMP; 889 else if (r < 320) 890 nextState = STATE_IDLE1; 891 else if (r < 480) 892 nextState = STATE_IDLE2; 893 else 894 break; 895 return STATE_STOP; 896 } 897 break; 898 case STATE_ATTACK : 899 if (nextState == STATE_NONE && (collide(target) & HIT_MASK)) { 900 bite(15, vec3(0.0f, -19.0f, 75.0f), 200.0f); 901 nextState = STATE_STOP; 902 } 903 break; 904 case STATE_LEFT : 905 case STATE_RIGHT : 906 return STATE_STOP; 907 default : ; 908 } 909 910 return state; 911 } 912 913 virtual void hit(float damage, Controller *enemy = NULL, TR::HitType hitType = TR::HIT_DEFAULT) { 914 Enemy::hit(damage, enemy, hitType); 915 flags.unused |= FLAG_ATTACK; 916 }; 917 getStateDeathGorilla918 virtual int getStateDeath() { 919 if (state == STATE_DEATH) 920 return state; 921 return animation.setAnim(ANIM_DEATH + rand() % 2); 922 } 923 strafeGorilla924 void strafe(int dir) { 925 switch (state) { 926 case STATE_STOP : 927 if (flags.unused & FLAG_LEFT) angle.y += PI * 0.5f; 928 if (flags.unused & FLAG_RIGHT) angle.y -= PI * 0.5f; 929 flags.unused &= ~(FLAG_LEFT | FLAG_RIGHT); 930 break; 931 case STATE_LEFT : 932 if (!(flags.unused & FLAG_LEFT)) { 933 flags.unused |= FLAG_LEFT; 934 angle.y -= PI * 0.5f; 935 } 936 break; 937 case STATE_RIGHT : 938 if (!(flags.unused & FLAG_RIGHT)) { 939 flags.unused |= FLAG_RIGHT; 940 angle.y += PI * 0.5f; 941 } 942 break; 943 default : ; 944 } 945 } 946 updateAnimationGorilla947 virtual void updateAnimation(bool commands) { 948 Enemy::updateAnimation(commands); 949 950 strafe(state); 951 952 if ((state == STATE_LEFT || state == STATE_RIGHT) && animation.isPrepareToNext && animation.anims[animation.next].state == STATE_STOP) 953 animation.rot = (state == STATE_LEFT ? -PI : PI) * 0.5f; 954 } 955 updatePositionGorilla956 virtual void updatePosition() { 957 turn(state == STATE_RUN, GORILLA_TURN_FAST); 958 959 if (state == STATE_DEATH) { 960 animation.overrideMask = 0; 961 return; 962 } 963 964 vec3 old = pos; 965 966 TR::Level::FloorInfo infoA, infoB; 967 getFloorInfo(getRoomIndex(), old, infoA); 968 old.y = infoA.floor; 969 970 Enemy::updatePosition(); 971 972 getFloorInfo(getRoomIndex(), pos, infoB); 973 974 if (infoB.floor < old.y - 384) 975 climb(old); 976 977 setOverrides(true, jointChest, jointHead); 978 lookAt(target); 979 } 980 climbGorilla981 void climb(const vec3 &old) { 982 int ox = int(old.x) / 1024; 983 int oz = int(old.z) / 1024; 984 985 int cx = int(pos.x) / 1024; 986 int cz = int(pos.z) / 1024; 987 988 if ((ox == cx) == (oz == cz)) 989 return; 990 991 strafe(STATE_STOP); 992 993 if (oz == cz) { 994 if (ox < cx) { 995 pos.x = float(cx * 1024 - 75); 996 angle.y = PI * 0.5f; 997 } else { 998 pos.x = float(ox * 1024 + 75); 999 angle.y = -PI * 0.5f; 1000 } 1001 } else { 1002 if (oz < cz) { 1003 pos.z = float(cz * 1024 - 75); 1004 angle.y = 0.0f; 1005 } else { 1006 pos.z = float(oz * 1024 + 75); 1007 angle.y = -PI; 1008 } 1009 } 1010 1011 pos.y = old.y; 1012 animation.setAnim(ANIM_CLIMB); 1013 } 1014 }; 1015 1016 1017 #define RAT_TURN_SLOW (DEG2RAD * 90) 1018 #define RAT_TURN_FAST (DEG2RAD * 180) 1019 #define RAT_DIST_BITE 341.0f 1020 #define RAT_DIST_ATTACK 1536.0f 1021 #define RAT_WAIT 0.01f 1022 #define RAT_DAMAGE 20 1023 1024 struct Rat : Enemy { 1025 1026 enum { 1027 HIT_MASK = 0x0300018F, 1028 }; 1029 1030 enum { 1031 ANIM_DEATH_LAND = 8, 1032 ANIM_DEATH_WATER = 2, 1033 }; 1034 1035 enum { 1036 // land 1037 STATE_NONE , 1038 STATE_STOP , 1039 STATE_ATTACK , 1040 STATE_RUN , 1041 STATE_BITE , 1042 STATE_DEATH , 1043 STATE_WAIT , 1044 // water 1045 STATE_WATER_SWIM = 1, 1046 STATE_WATER_BITE , 1047 STATE_WATER_DEATH , 1048 }; 1049 1050 int modelLand, modelWater; 1051 RatRat1052 Rat(IGame *game, int entity) : Enemy(game, entity, 5, 204, 200.0f, 0.25f) { 1053 hitSound = TR::SND_HIT_RAT; 1054 jointChest = 1; 1055 jointHead = 2; 1056 1057 modelLand = level->getModelIndex(TR::Entity::ENEMY_RAT_LAND) - 1; 1058 modelWater = level->getModelIndex(TR::Entity::ENEMY_RAT_WATER) - 1; 1059 } 1060 getModelRat1061 const virtual TR::Model* getModel() { 1062 bool water = getRoom().flags.water || modelWater == -1; 1063 stand = water ? STAND_ONWATER : STAND_GROUND; 1064 1065 int modelIndex = water ? modelWater : modelLand; 1066 1067 if (modelIndex == -1) { 1068 water = modelWater != -1; 1069 modelIndex = water ? modelWater : modelLand; 1070 } 1071 1072 ASSERT(modelIndex > -1); 1073 const TR::Model *model = &level->models[modelIndex]; 1074 if (animation.model != model) { 1075 targetBox = TR::NO_BOX; 1076 animation.setModel(model); 1077 1078 int16 rIndex = getRoomIndex(); 1079 if (water) { 1080 TR::Room::Sector *sector = level->getWaterLevelSector(rIndex, pos); 1081 if (sector) { 1082 pos.y = float(sector->ceiling * 256); 1083 roomIndex = rIndex; 1084 } 1085 } else { 1086 TR::Room::Sector *sector = level->getSector(rIndex, pos); 1087 if (sector) { 1088 pos.y = float(sector->floor * 256); 1089 roomIndex = rIndex; 1090 } 1091 } 1092 1093 nextState = STATE_NONE; 1094 state = STATE_NONE; 1095 1096 if (health <= 0.0f) { 1097 getStateDeath(); 1098 animation.goEnd(false); 1099 } 1100 1101 updateZone(); 1102 } 1103 return animation.model; 1104 } 1105 getStateGroundRat1106 virtual int getStateGround() { 1107 if (!think(false)) 1108 return state; 1109 1110 if (nextState == state) 1111 nextState = STATE_NONE; 1112 1113 switch (state) { 1114 case STATE_STOP : 1115 if (nextState != STATE_NONE) 1116 return nextState; 1117 if (targetCanAttack && targetDist < RAT_DIST_BITE) 1118 return STATE_BITE; 1119 return STATE_RUN; 1120 case STATE_RUN : 1121 if (targetInView && (collide(target) & HIT_MASK)) 1122 return STATE_STOP; 1123 if (targetCanAttack && targetDist < RAT_DIST_ATTACK) 1124 return STATE_ATTACK; 1125 if (targetInView && randf() < RAT_WAIT) { 1126 nextState = STATE_WAIT; 1127 return STATE_STOP; 1128 } 1129 break; 1130 case STATE_ATTACK : 1131 case STATE_BITE : 1132 if (nextState == STATE_NONE && targetInView && (collide(target) & HIT_MASK)) { 1133 bite(3, vec3(0.0f, -11.0f, 108.0f), RAT_DAMAGE); 1134 nextState = state == STATE_ATTACK ? STATE_RUN : STATE_STOP; 1135 } 1136 break; 1137 case STATE_WAIT : 1138 if (mood == MOOD_SLEEP || randf() < RAT_WAIT) 1139 return STATE_STOP; 1140 default : ; 1141 } 1142 1143 return state; 1144 } 1145 getStateOnwaterRat1146 virtual int getStateOnwater() { 1147 if (!think(false)) 1148 return state; 1149 1150 if (nextState == state) 1151 nextState = STATE_NONE; 1152 1153 if (animation.frameIndex % 4 == 0) 1154 game->waterDrop(getJoint(jointHead).pos, 96.0f, 0.02f); 1155 1156 switch (state) { 1157 case STATE_WATER_SWIM : 1158 if (targetInView && (collide(target) & HIT_MASK)) 1159 return STATE_WATER_BITE; 1160 break; 1161 case STATE_WATER_BITE : 1162 if (nextState == STATE_NONE && targetInView && (collide(target) & HIT_MASK)) { 1163 game->waterDrop(getJoint(jointHead).pos, 256.0f, 0.2f); 1164 bite(3, vec3(0.0f, -11.0f, 108.0f), RAT_DAMAGE); 1165 nextState = STATE_WATER_SWIM; 1166 } 1167 return STATE_NONE; 1168 default : ; 1169 } 1170 1171 return state; 1172 } 1173 updatePositionRat1174 virtual void updatePosition() { 1175 turn((stand == STAND_GROUND && state == STATE_RUN) || (stand == STAND_ONWATER && state == STATE_WATER_SWIM), RAT_TURN_FAST); 1176 1177 if (state == STATE_DEATH) { 1178 animation.overrideMask = 0; 1179 return; 1180 } 1181 1182 Enemy::updatePosition(); 1183 setOverrides(state != STATE_DEATH, jointChest, jointHead); 1184 lookAt(target); 1185 } 1186 getStateDeathRat1187 virtual int getStateDeath() { 1188 bool water = getRoom().flags.water; 1189 if ((water && state == STATE_WATER_DEATH) || (!water && state == STATE_DEATH)) 1190 return state; 1191 return animation.setAnim(water ? ANIM_DEATH_WATER : ANIM_DEATH_LAND); 1192 } 1193 }; 1194 1195 1196 #define CROCODILE_TURN_SLOW (DEG2RAD * 90) 1197 #define CROCODILE_TURN_FAST (DEG2RAD * 180) 1198 #define CROCODILE_DIST_BITE 435.0f 1199 #define CROCODILE_DIST_TURN (1024 * 3) 1200 #define CROCODILE_LIFT_SPEED 960.0f 1201 #define CROCODILE_DAMAGE 25 1202 1203 struct Crocodile : Enemy { 1204 1205 enum { 1206 HIT_MASK = 0x000003FC, 1207 }; 1208 1209 enum { 1210 ANIM_DEATH_LAND = 11, 1211 ANIM_DEATH_WATER = 4, 1212 }; 1213 1214 enum { 1215 // land 1216 STATE_NONE , 1217 STATE_STOP , 1218 STATE_RUN , 1219 STATE_WALK , 1220 STATE_TURN , 1221 STATE_BITE , 1222 STATE_UNUSED , 1223 STATE_DEATH , 1224 // water 1225 STATE_WATER_SWIM = 1, 1226 STATE_WATER_BITE , 1227 STATE_WATER_DEATH , 1228 }; 1229 1230 int modelLand, modelWater; 1231 CrocodileCrocodile1232 Crocodile(IGame *game, int entity) : Enemy(game, entity, 20, 341, 600.0f, 0.25f) { 1233 jointChest = 1; 1234 jointHead = 8; 1235 1236 modelLand = level->getModelIndex(TR::Entity::ENEMY_CROCODILE_LAND) - 1; 1237 modelWater = level->getModelIndex(TR::Entity::ENEMY_CROCODILE_WATER) - 1; 1238 1239 bool water = getRoom().flags.water || modelWater == -1; 1240 flying = water; 1241 stand = water ? STAND_UNDERWATER : STAND_GROUND; 1242 } 1243 getModelCrocodile1244 const virtual TR::Model* getModel() { 1245 bool water = getRoom().flags.water || modelWater == -1; 1246 stand = water ? STAND_UNDERWATER : STAND_GROUND; 1247 1248 int modelIndex = water ? modelWater : modelLand; 1249 1250 if (modelIndex == -1) { 1251 water = modelWater != -1; 1252 modelIndex = water ? modelWater : modelLand; 1253 } 1254 1255 ASSERT(modelIndex > -1); 1256 const TR::Model *model = &level->models[modelIndex]; 1257 if (animation.model != model) { 1258 targetBox = TR::NO_BOX; 1259 animation.setModel(model); 1260 flying = water; 1261 1262 int16 rIndex = getRoomIndex(); 1263 if (water) { 1264 TR::Room::Sector *sector = level->getWaterLevelSector(rIndex, pos); 1265 if (sector) { 1266 pos.y = float(sector->ceiling * 256); 1267 roomIndex = rIndex; 1268 } 1269 } else { 1270 TR::Room::Sector *sector = level->getSector(rIndex, pos); 1271 if (sector) { 1272 pos.y = float(sector->floor * 256); 1273 roomIndex = rIndex; 1274 } 1275 } 1276 1277 nextState = STATE_NONE; 1278 state = STATE_NONE; 1279 1280 if (health <= 0.0f) { 1281 getStateDeath(); 1282 animation.goEnd(false); 1283 } 1284 1285 updateZone(); 1286 } 1287 return animation.model; 1288 } 1289 getStateGroundCrocodile1290 virtual int getStateGround() { 1291 if (!think(true)) 1292 return state; 1293 1294 if (nextState == state) 1295 nextState = STATE_NONE; 1296 1297 switch (state) { 1298 case STATE_STOP : 1299 if (targetCanAttack && targetDist < CROCODILE_DIST_BITE) 1300 return STATE_BITE; 1301 switch (mood) { 1302 case MOOD_ESCAPE : return STATE_RUN; 1303 case MOOD_ATTACK : return (targetInView || targetDist < CROCODILE_DIST_TURN) ? STATE_RUN : STATE_TURN; 1304 case MOOD_STALK : return STATE_WALK; 1305 default : return state; 1306 } 1307 case STATE_RUN : 1308 if (targetInView && (collide(target) & HIT_MASK)) 1309 return STATE_STOP; 1310 switch (mood) { 1311 case MOOD_SLEEP : return STATE_STOP; 1312 case MOOD_STALK : return STATE_WALK; 1313 case MOOD_ATTACK : if (targetDist > CROCODILE_DIST_TURN && !targetInView) return STATE_STOP; 1314 default : return state; 1315 } 1316 case STATE_WALK : 1317 if (targetInView && (collide(target) & HIT_MASK)) 1318 return STATE_STOP; 1319 switch (mood) { 1320 case MOOD_SLEEP : return STATE_STOP; 1321 case MOOD_ATTACK : 1322 case MOOD_ESCAPE : return STATE_RUN; 1323 default : return state; 1324 } 1325 case STATE_TURN : 1326 return targetInView ? STATE_WALK : state; 1327 case STATE_BITE : 1328 if (nextState == STATE_NONE) { 1329 bite(9, vec3(5.0f, -21.0f, 467.0f), CROCODILE_DAMAGE); 1330 nextState = STATE_STOP; 1331 } 1332 break; 1333 default : ; 1334 } 1335 1336 return state; 1337 } 1338 getStateUnderwaterCrocodile1339 virtual int getStateUnderwater() { 1340 if (!think(false)) 1341 return state; 1342 1343 if (nextState == state) 1344 nextState = STATE_NONE; 1345 1346 if (animation.frameIndex % 4 == 0) 1347 game->waterDrop(getJoint(jointHead).pos, 96.0f, 0.02f); 1348 1349 switch (state) { 1350 case STATE_WATER_SWIM : 1351 if (targetInView && collide(target)) 1352 return STATE_WATER_BITE; 1353 break; 1354 case STATE_WATER_BITE : 1355 if (collide(target)) { 1356 if (nextState != STATE_NONE) 1357 return state; 1358 bite(9, vec3(5.0f, -21.0f, 467.0f), CROCODILE_DAMAGE); 1359 nextState = STATE_WATER_SWIM; 1360 } 1361 return STATE_WATER_SWIM; 1362 default : ; 1363 } 1364 1365 return state; 1366 } 1367 updatePositionCrocodile1368 virtual void updatePosition() { 1369 if (state == STATE_TURN) 1370 angle.y += CROCODILE_TURN_FAST * Core::deltaTime; 1371 else 1372 turn((stand == STAND_GROUND && (state == STATE_RUN || state == STATE_WALK)) || (stand == STAND_UNDERWATER && state == STATE_WATER_SWIM), CROCODILE_TURN_FAST); 1373 angle.z = 0.0f; 1374 1375 if (state == STATE_DEATH) { 1376 animation.overrideMask = 0; 1377 return; 1378 } 1379 1380 if (flying) { 1381 lift(waypoint.y - pos.y, CROCODILE_LIFT_SPEED); 1382 int16 rIndex = getRoomIndex(); 1383 TR::Room::Sector *sector = level->getWaterLevelSector(rIndex, pos); 1384 if (sector) { 1385 float waterLevel = float(sector->ceiling * 256) + 256; 1386 if (pos.y < waterLevel) 1387 pos.y = waterLevel; 1388 } 1389 } 1390 1391 Enemy::updatePosition(); 1392 setOverrides(state != STATE_DEATH, jointChest, jointHead); 1393 lookAt(target); 1394 } 1395 getStateDeathCrocodile1396 virtual int getStateDeath() { 1397 bool water = getRoom().flags.water; 1398 if ((water && state == STATE_WATER_DEATH) || (!water && state == STATE_DEATH)) 1399 return state; 1400 return animation.setAnim(water ? ANIM_DEATH_WATER : ANIM_DEATH_LAND); 1401 } 1402 }; 1403 1404 1405 #define BEAR_DIST_EAT 768 1406 #define BEAR_DIST_HOWL 2048 1407 #define BEAR_DIST_BITE 1024 1408 #define BEAR_DIST_ATTACK 600 1409 1410 #define BEAR_TURN_FAST (DEG2RAD * 150) 1411 #define BEAR_TURN_SLOW (DEG2RAD * 60) 1412 1413 struct Bear : Enemy { 1414 1415 enum { 1416 HIT_MASK = 0x2406C, // front legs and head 1417 }; 1418 1419 enum { 1420 ANIM_DEATH_HIND = 19, 1421 ANIM_DEATH = 20, 1422 }; 1423 1424 enum { 1425 STATE_NONE = -1, 1426 STATE_WALK , 1427 STATE_STOP , 1428 STATE_HIND , 1429 STATE_RUN , 1430 STATE_HOWL , 1431 STATE_GROWL , 1432 STATE_BITE , 1433 STATE_ATTACK , 1434 STATE_EAT , 1435 STATE_DEATH , 1436 }; 1437 BearBear1438 Bear(IGame *game, int entity) : Enemy(game, entity, 20, 341, 500.0f, 0.5f) { 1439 jointChest = 13; 1440 jointHead = 14; 1441 hitSound = TR::SND_HIT_BEAR; 1442 nextState = STATE_NONE; 1443 } 1444 getStateGroundBear1445 virtual int getStateGround() { 1446 if (!flags.active) 1447 return state; 1448 1449 if (!think(true)) 1450 return state; 1451 1452 if (nextState == state) 1453 nextState = STATE_NONE; 1454 1455 switch (state) { 1456 case STATE_WALK : 1457 if (nextState != STATE_NONE) return STATE_STOP; 1458 if (targetDead && targetInView && (collide(target) & HIT_MASK)) 1459 return nextState = STATE_STOP; // eat lara! >:E 1460 if (mood != MOOD_SLEEP) { 1461 if (mood == MOOD_ESCAPE) 1462 nextState = STATE_NONE; 1463 return STATE_STOP; 1464 } else if (randf() < 0.003f) { 1465 nextState = STATE_GROWL; 1466 return STATE_STOP; 1467 } 1468 break; 1469 case STATE_STOP : 1470 if (targetDead) 1471 return (targetDist < BEAR_DIST_EAT && targetCanAttack) ? STATE_EAT : STATE_WALK; 1472 else 1473 return nextState != STATE_NONE ? nextState : (mood == MOOD_SLEEP ? STATE_WALK : STATE_RUN); 1474 case STATE_HIND : 1475 if (wound) { 1476 nextState = STATE_NONE; 1477 return STATE_HOWL; 1478 } 1479 1480 if (targetInView && (collide(target) & HIT_MASK)) return STATE_HOWL; 1481 1482 if (mood == MOOD_ESCAPE) 1483 nextState = STATE_NONE; 1484 else if (mood == MOOD_SLEEP || randf() < 0.003f) 1485 nextState = STATE_GROWL; 1486 else if (targetDist > BEAR_DIST_HOWL || randf() < 0.05f) 1487 nextState = STATE_STOP; 1488 1489 return STATE_HOWL; 1490 break; 1491 case STATE_RUN : 1492 if (collide(target) & HIT_MASK) 1493 target->hit(3, this); 1494 if (targetDead || mood == MOOD_SLEEP) 1495 return STATE_STOP; 1496 if (nextState != STATE_NONE) return STATE_STOP; 1497 if (targetInView) { 1498 if (!wound && targetDist < BEAR_DIST_HOWL && randf() < 0.025f) { 1499 nextState = STATE_HOWL; 1500 return STATE_STOP; 1501 } 1502 if (targetDist < BEAR_DIST_BITE) return STATE_BITE; 1503 } 1504 break; 1505 case STATE_HOWL : 1506 if (wound) { 1507 nextState = STATE_NONE; 1508 return STATE_STOP; 1509 } 1510 if (nextState != STATE_NONE) return nextState; 1511 if (mood == MOOD_SLEEP || mood == MOOD_ESCAPE) return STATE_STOP; 1512 if (targetDist < BEAR_DIST_ATTACK) return STATE_ATTACK; 1513 return STATE_HIND; 1514 case STATE_BITE : 1515 case STATE_ATTACK : 1516 if (nextState == STATE_NONE && (collide(target) & HIT_MASK)) { 1517 bite(14, vec3(0.0f, 96.0f, 335.0f), state == STATE_BITE ? 200.0f : 400.0f); 1518 nextState = state == STATE_BITE ? STATE_STOP : STATE_HOWL; 1519 } 1520 break; 1521 } 1522 1523 return state; 1524 } 1525 getStateDeathBear1526 virtual int getStateDeath() { 1527 switch (state) { 1528 case STATE_HIND : return STATE_HOWL; 1529 case STATE_RUN : 1530 case STATE_WALK : return STATE_STOP; 1531 case STATE_HOWL : 1532 case STATE_STOP : return STATE_DEATH; 1533 } 1534 return state;// == STATE_DEATH ? state : animation.setAnim(ANIM_DEATH); 1535 } 1536 updatePositionBear1537 virtual void updatePosition() { 1538 turn(state == STATE_RUN || state == STATE_WALK || state == STATE_HIND, state == STATE_RUN ? BEAR_TURN_FAST : BEAR_TURN_SLOW); 1539 1540 if (state == STATE_DEATH) { 1541 animation.overrideMask = 0; 1542 return; 1543 } 1544 1545 Enemy::updatePosition(); 1546 setOverrides(state == STATE_RUN || state == STATE_WALK || state == STATE_HIND, jointChest, jointHead); 1547 lookAt(target); 1548 } 1549 }; 1550 1551 1552 #define BAT_TURN_SPEED (DEG2RAD * 300) 1553 #define BAT_LIFT_SPEED 512.0f 1554 1555 struct Bat : Enemy { 1556 1557 enum { 1558 ANIM_DEATH = 4, 1559 }; 1560 1561 enum { 1562 STATE_NONE, 1563 STATE_AWAKE, 1564 STATE_FLY, 1565 STATE_ATTACK, 1566 STATE_CIRCLING, 1567 STATE_DEATH, 1568 }; 1569 BatBat1570 Bat(IGame *game, int entity) : Enemy(game, entity, 1, 102, 0.0f, 0.03f) { 1571 stand = STAND_AIR; 1572 stepHeight = 20 * 1024; 1573 dropHeight = -20 * 1024; 1574 jointHead = 4; 1575 } 1576 getStateAirBat1577 virtual int getStateAir() { 1578 if (!flags.active) { 1579 animation.time = 0.0f; 1580 animation.dir = 0.0f; 1581 return STATE_AWAKE; 1582 } 1583 1584 if (!think(false)) 1585 return state; 1586 1587 switch (state) { 1588 case STATE_AWAKE : return STATE_FLY; 1589 case STATE_ATTACK : 1590 if (!collide(target)) { 1591 mood = MOOD_SLEEP; 1592 return STATE_FLY; 1593 } else 1594 bite(4, vec3(0.0f, 16.0f, 45.0f), 2); 1595 break; 1596 case STATE_FLY : 1597 if (collide(target)) { 1598 mood = MOOD_ATTACK; 1599 return STATE_ATTACK; 1600 } 1601 break; 1602 } 1603 1604 return state; 1605 } 1606 getStateDeathBat1607 virtual int getStateDeath() { 1608 return state == STATE_DEATH ? state : animation.setAnim(ANIM_DEATH); 1609 } 1610 updatePositionBat1611 virtual void updatePosition() { 1612 turn(state == STATE_FLY || state == STATE_ATTACK, BAT_TURN_SPEED); 1613 1614 if (flying) { 1615 float wy = waypoint.y - (target->stand != STAND_ONWATER ? 765.0f : 64.0f); 1616 lift(wy - pos.y, BAT_LIFT_SPEED); 1617 } 1618 Enemy::updatePosition(); 1619 } 1620 1621 virtual void deactivate(bool removeFromList = false) { 1622 if (health <= 0.0f) { 1623 TR::Level::FloorInfo info; 1624 getFloorInfo(getRoomIndex(), pos, info); 1625 if (info.floor > pos.y) 1626 return; 1627 pos.y = info.floor; 1628 } 1629 Enemy::deactivate(removeFromList); 1630 } 1631 }; 1632 1633 1634 #define REX_DIST_BITE 1500 1635 #define REX_DIST_BITE_MAX 4096 1636 #define REX_DIST_WALK 5120 1637 #define REX_TURN_FAST (DEG2RAD * 120) 1638 #define REX_TURN_SLOW (DEG2RAD * 60) 1639 #define REX_DAMAGE 1000 1640 #define REX_DAMAGE_WALK 1 1641 #define REX_DAMAGE_RUN 10 1642 1643 struct Rex : Enemy { 1644 1645 enum { 1646 HIT_MASK = (1 << 12) | (1 << 13), // head 1647 }; 1648 1649 enum { 1650 STATE_NONE, 1651 STATE_STOP, 1652 STATE_WALK, 1653 STATE_RUN, 1654 STATE_UNUSED, 1655 STATE_DEATH, 1656 STATE_BAWL, 1657 STATE_BITE, 1658 STATE_FATAL, 1659 }; 1660 RexRex1661 Rex(IGame *game, int entity) : Enemy(game, entity, 100, 341, 2000.0f, 1.0f) { 1662 jointChest = 10; 1663 jointHead = 12; 1664 nextState = STATE_NONE; 1665 } 1666 getStateGroundRex1667 virtual int getStateGround() { 1668 if (!flags.active) 1669 return state; 1670 1671 if (!think(true)) 1672 return state; 1673 1674 if (nextState == state) 1675 nextState = STATE_NONE; 1676 1677 if (targetDead) { 1678 return (state == STATE_STOP || state == STATE_WALK) ? STATE_WALK : STATE_STOP; 1679 if (state != STATE_STOP) return STATE_STOP; 1680 return STATE_WALK; 1681 } 1682 1683 int mask = collide(target); 1684 1685 // if Lara is behind and watching Rex we need to rotate 1686 bool walk = targetFromView && !targetInView && mood != MOOD_ESCAPE; 1687 if (!walk && targetCanAttack && targetDist > REX_DIST_BITE && targetDist < REX_DIST_BITE_MAX) 1688 walk = true; 1689 1690 switch (state) { 1691 case STATE_STOP : 1692 if (nextState != STATE_NONE) return nextState; 1693 if (targetCanAttack && targetDist < REX_DIST_BITE) return STATE_BITE; 1694 if (mood == MOOD_SLEEP || walk) return STATE_WALK; 1695 return STATE_RUN; 1696 case STATE_WALK : 1697 if (mask) target->hit(REX_DAMAGE_WALK, this); 1698 if (mood != MOOD_SLEEP && !walk) return STATE_STOP; 1699 if (targetInView && randf() < 0.015f) { 1700 nextState = STATE_BAWL; 1701 return STATE_STOP; 1702 } 1703 break; 1704 case STATE_RUN : 1705 if (mask) target->hit(REX_DAMAGE_RUN, this); 1706 if ((targetCanAttack && targetDist < REX_DIST_WALK) || walk) 1707 return STATE_STOP; 1708 if (targetInView && mood != MOOD_ESCAPE && randf() < 0.015f) { 1709 nextState = STATE_BAWL; 1710 return STATE_STOP; 1711 } 1712 if (mood == MOOD_SLEEP) 1713 return STATE_STOP; 1714 break; 1715 case STATE_BITE : 1716 if (mask & HIT_MASK) { 1717 target->hit(REX_DAMAGE, this, TR::HIT_REX); 1718 return STATE_FATAL; 1719 } 1720 nextState = STATE_WALK; 1721 break; 1722 } 1723 1724 return state; 1725 } 1726 getStateDeathRex1727 virtual int getStateDeath() { 1728 return state == STATE_STOP ? STATE_DEATH : STATE_STOP; 1729 } 1730 updatePositionRex1731 virtual void updatePosition() { 1732 if (state == STATE_DEATH) { 1733 animation.overrideMask = 0; 1734 angle.z = 0.0f; 1735 return; 1736 } 1737 1738 turn(state == STATE_RUN || state == STATE_WALK, state == STATE_RUN ? REX_TURN_FAST : REX_TURN_SLOW); 1739 1740 Enemy::updatePosition(); 1741 setOverrides(true, jointChest, jointHead); 1742 lookAt(target); 1743 } 1744 }; 1745 1746 #define RAPTOR_DIST_BITE 680 1747 #define RAPTOR_DIST_ATTACK (1024 + 512) 1748 1749 #define RAPTOR_TURN_FAST (DEG2RAD * 120) 1750 #define RAPTOR_TURN_SLOW (DEG2RAD * 30) 1751 1752 struct Raptor : Enemy { 1753 1754 enum { 1755 HIT_MASK = 0xFF7C00, // hands and head 1756 }; 1757 1758 enum { 1759 ANIM_DEATH_1 = 9, 1760 ANIM_DEATH_2 = 10, 1761 }; 1762 1763 enum { 1764 STATE_NONE = -1, 1765 STATE_DEATH, 1766 STATE_STOP, 1767 STATE_WALK, 1768 STATE_RUN, 1769 STATE_ATTACK_1, 1770 STATE_UNUSED, 1771 STATE_BAWL, 1772 STATE_ATTACK_2, 1773 STATE_BITE, 1774 }; 1775 RaptorRaptor1776 Raptor(IGame *game, int entity) : Enemy(game, entity, 20, 341, 400.0f, 0.5f) { 1777 jointChest = -1; 1778 jointHead = 21; 1779 nextState = STATE_NONE; 1780 } 1781 getStateGroundRaptor1782 virtual int getStateGround() { 1783 if (!flags.active) 1784 return state; 1785 1786 if (!think(true)) 1787 return state; 1788 1789 if (nextState == state) 1790 nextState = STATE_NONE; 1791 1792 if (targetDead) { 1793 return (state == STATE_STOP || state == STATE_WALK) ? STATE_WALK : STATE_STOP; 1794 if (state != STATE_STOP) return STATE_STOP; 1795 return STATE_WALK; 1796 } 1797 1798 int mask = collide(target); 1799 1800 switch (state) { 1801 case STATE_STOP : 1802 if (nextState != STATE_NONE) return nextState; 1803 if ((mask & HIT_MASK) || (targetCanAttack && targetDist < RAPTOR_DIST_BITE)) return STATE_BITE; 1804 if (targetCanAttack && targetDist < RAPTOR_DIST_ATTACK) return STATE_ATTACK_1; 1805 if (mood == MOOD_SLEEP) return STATE_WALK; 1806 return STATE_RUN; 1807 case STATE_WALK : 1808 if (nextState != STATE_NONE) return STATE_STOP; 1809 if (mood != MOOD_SLEEP) return STATE_STOP; 1810 if (targetInView && randf() < 0.01f) { 1811 nextState = STATE_BAWL; 1812 return STATE_STOP; 1813 } 1814 break; 1815 case STATE_RUN : 1816 if (nextState != STATE_NONE) return STATE_STOP; 1817 if (mask & HIT_MASK) return STATE_STOP; 1818 if (targetCanAttack && targetDist < RAPTOR_DIST_ATTACK) 1819 return (randf() < 0.25) ? STATE_STOP : STATE_ATTACK_2; 1820 if (mood == MOOD_ESCAPE && targetInView) { 1821 nextState = STATE_BAWL; 1822 return STATE_STOP; 1823 } 1824 if (mood == MOOD_SLEEP) 1825 return STATE_STOP; 1826 break; 1827 case STATE_ATTACK_1 : 1828 case STATE_ATTACK_2 : 1829 case STATE_BITE : 1830 if (nextState == STATE_NONE && targetInView && (mask & HIT_MASK)) { 1831 bite(22, vec3(0.0f, 66.0f, 318.0f), 100); 1832 nextState = state == STATE_ATTACK_2 ? STATE_RUN : STATE_STOP; 1833 } 1834 break; 1835 } 1836 1837 return state; 1838 } 1839 getStateDeathRaptor1840 virtual int getStateDeath() { 1841 if (state == STATE_DEATH) return state; 1842 return animation.setAnim((rand() % 2) ? ANIM_DEATH_1 : ANIM_DEATH_2); 1843 } 1844 updatePositionRaptor1845 virtual void updatePosition() { 1846 if (state == STATE_DEATH) { 1847 animation.overrideMask = 0; 1848 angle.z = 0.0f; 1849 return; 1850 } 1851 1852 turn(state == STATE_RUN || state == STATE_WALK, state == STATE_RUN ? RAPTOR_TURN_FAST : RAPTOR_TURN_SLOW); 1853 1854 Enemy::updatePosition(); 1855 setOverrides(true, jointChest, jointHead); 1856 lookAt(target); 1857 } 1858 }; 1859 1860 1861 #define MUTANT_TURN_FAST (DEG2RAD * 180) 1862 #define MUTANT_TURN_SLOW (DEG2RAD * 60) 1863 #define MUTANT_LIFT_SPEED 512.0f 1864 #define MUTANT_DIST_ATTACK_1 600 1865 #define MUTANT_DIST_ATTACK_2 (2048 + 512) 1866 #define MUTANT_DIST_ATTACK_3 300 1867 #define MUTANT_DIST_SHOT 3840 1868 #define MUTANT_DIST_STALK (4096 + 512) 1869 #define MUTANT_PART_DAMAGE 100 1870 1871 struct Mutant : Enemy { 1872 1873 enum { 1874 HIT_MASK = 0x0678, 1875 }; 1876 1877 enum { 1878 STATE_NONE, 1879 STATE_STOP, 1880 STATE_WALK, 1881 STATE_RUN, 1882 STATE_ATTACK_1, 1883 STATE_DEATH, 1884 STATE_LOOKING, 1885 STATE_ATTACK_2, 1886 STATE_ATTACK_3, 1887 STATE_AIM_1, 1888 STATE_AIM_2, 1889 STATE_FIRE, 1890 STATE_IDLE, 1891 STATE_FLY, 1892 }; 1893 1894 enum { 1895 FLAG_FLY = 1, 1896 FLAG_BULLET_1 = 2, 1897 FLAG_BULLET_2 = 4, 1898 }; 1899 MutantMutant1900 Mutant(IGame *game, int entity) : Enemy(game, entity, 50, 341, 150.0f, 1.0f) { 1901 if (getEntity().type != TR::Entity::ENEMY_MUTANT_1) { 1902 initMeshOverrides(); 1903 layers[0].mask = 0xffe07fff; 1904 aggression = 0.25f; 1905 } 1906 1907 flags.unused = 0; 1908 jointChest = 1; 1909 jointHead = 2; 1910 } 1911 setSaveDataMutant1912 virtual void setSaveData(const SaveEntity &data) { 1913 Enemy::setSaveData(data); 1914 if (flags.invisible) 1915 deactivate(true); 1916 } 1917 updateMutant1918 virtual void update() { 1919 bool exploded = explodeMask != 0; 1920 1921 if (health <= 0.0f && !exploded) { 1922 game->playSound(TR::SND_MUTANT_DEATH, pos, Sound::PAN); 1923 explode(0xffffffff, MUTANT_PART_DAMAGE); 1924 } 1925 1926 Enemy::update(); 1927 1928 if (exploded && !explodeMask) { 1929 deactivate(true); 1930 flags.invisible = true; 1931 } 1932 } 1933 getStateGroundMutant1934 virtual int getStateGround() { 1935 if (state == STATE_FLY) { 1936 stand = STAND_AIR; 1937 flying = true; 1938 updateZone(); 1939 return getStateAir(); 1940 } 1941 1942 stepHeight = 256; 1943 dropHeight = -stepHeight; 1944 1945 if (!think(true)) 1946 return state; 1947 1948 if (nextState == state) 1949 nextState = STATE_NONE; 1950 1951 if (getEntity().type != TR::Entity::ENEMY_MUTANT_3) { 1952 if (flags.unused & (FLAG_BULLET_1 | FLAG_BULLET_2)) { 1953 if (targetAngle > PI * 0.25f) 1954 flags.unused &= ~(FLAG_BULLET_1 | FLAG_BULLET_2); 1955 } else { 1956 if (targetAngle < PI * 0.25f && state != STATE_FIRE && (targetDist > MUTANT_DIST_SHOT || zone != target->zone) && targetIsVisible(MAX_SHOT_DIST)) 1957 flags.unused |= (rand() % 2) ? FLAG_BULLET_1 : FLAG_BULLET_2; 1958 } 1959 } 1960 1961 if (getEntity().type == TR::Entity::ENEMY_MUTANT_1) { // flying mutant 1962 if (mood == MOOD_ESCAPE || (zone != target->zone && !(flags.unused & (FLAG_BULLET_1 | FLAG_BULLET_2)))) { 1963 flags.unused |= FLAG_FLY; 1964 } 1965 } 1966 1967 int mask = collide(target); 1968 1969 switch (state) { 1970 case STATE_STOP : 1971 if (flags.unused & FLAG_FLY) 1972 return STATE_FLY; 1973 if ((targetCanAttack && targetDist < MUTANT_DIST_ATTACK_3) || (mask & HIT_MASK)) 1974 return STATE_ATTACK_3; 1975 if ((targetCanAttack && targetDist < MUTANT_DIST_ATTACK_1)) 1976 return STATE_ATTACK_1; 1977 if (flags.unused & FLAG_BULLET_1) 1978 return STATE_AIM_1; 1979 if (flags.unused & FLAG_BULLET_2) 1980 return STATE_AIM_2; 1981 if (mood == MOOD_SLEEP || (mood == MOOD_STALK && targetDist < MUTANT_DIST_STALK)) 1982 return STATE_LOOKING; 1983 return STATE_RUN; 1984 case STATE_WALK : 1985 if (flags.unused) 1986 return STATE_STOP; 1987 if (mood == MOOD_ATTACK || mood == MOOD_ESCAPE) 1988 return STATE_STOP; 1989 if (mood == MOOD_SLEEP || (mood == MOOD_STALK && target->zone != zone)) { 1990 if (rand() < 50) 1991 return STATE_LOOKING; 1992 } else if (mood == MOOD_STALK && targetDist > MUTANT_DIST_STALK) 1993 return STATE_STOP; 1994 break; 1995 case STATE_RUN : 1996 if (flags.unused & FLAG_FLY) 1997 return STATE_STOP; 1998 if (mask & HIT_MASK) 1999 return STATE_STOP; 2000 if (targetCanAttack && targetDist < MUTANT_DIST_ATTACK_1) 2001 return STATE_STOP; 2002 if (targetInView && targetDist < MUTANT_DIST_ATTACK_2) 2003 return STATE_ATTACK_2; 2004 if (flags.unused & (FLAG_BULLET_1 | FLAG_BULLET_2)) 2005 return STATE_STOP; 2006 if (mood == MOOD_SLEEP || (mood == MOOD_STALK && targetDist < MUTANT_DIST_STALK)) 2007 return STATE_STOP; 2008 break; 2009 case STATE_LOOKING : 2010 if (flags.unused) 2011 return STATE_STOP; 2012 switch (mood) { 2013 case MOOD_SLEEP : 2014 if (rand() < 256) 2015 return STATE_WALK; 2016 break; 2017 case MOOD_STALK : 2018 if (targetDist < MUTANT_DIST_STALK) { 2019 if (target->zone == zone && rand() < 256) 2020 return STATE_WALK; 2021 } else 2022 return STATE_STOP; 2023 break; 2024 case MOOD_ATTACK : 2025 case MOOD_ESCAPE : 2026 return STATE_STOP; 2027 } 2028 case STATE_ATTACK_1 : 2029 case STATE_ATTACK_2 : 2030 case STATE_ATTACK_3 : 2031 if (nextState == STATE_NONE && (mask & HIT_MASK)) { 2032 float damage = state == STATE_ATTACK_1 ? 150.0f : (state == STATE_ATTACK_2 ? 100.0f : 200.0f); 2033 bite(10, vec3(-27.0f, 98.0f, 0.0f), damage); 2034 nextState = STATE_STOP; 2035 } 2036 break; 2037 case STATE_AIM_1 : 2038 return (flags.unused & FLAG_BULLET_1) ? STATE_FIRE : STATE_STOP; 2039 case STATE_AIM_2 : 2040 return (flags.unused & FLAG_BULLET_2) ? STATE_FIRE : STATE_STOP; 2041 case STATE_FIRE : 2042 if (flags.unused & FLAG_BULLET_1) 2043 shot(TR::Entity::MUTANT_BULLET, 9, vec3(-35.0f, 269.0f, 0.0f)); 2044 if (flags.unused & FLAG_BULLET_2) 2045 shot(TR::Entity::CENTAUR_BULLET, 14, vec3(51.0f, 213.0f, 0.0f)); 2046 flags.unused &= ~(FLAG_BULLET_1 | FLAG_BULLET_2); 2047 break; 2048 case STATE_IDLE : 2049 return STATE_STOP; 2050 default : ; 2051 } 2052 2053 return state; 2054 } 2055 getStateAirMutant2056 virtual int getStateAir() { 2057 if (state != STATE_FLY) { 2058 stand = STAND_GROUND; 2059 flying = false; 2060 updateZone(); 2061 return getStateGround(); 2062 } 2063 2064 stepHeight = 30 * 1024; 2065 dropHeight = -stepHeight; 2066 2067 if (!think(true)) 2068 return state; 2069 2070 if ((flags.unused & FLAG_FLY) && mood != MOOD_ESCAPE && zone == target->zone) 2071 flags.unused &= ~FLAG_FLY; 2072 2073 if (!(flags.unused & FLAG_FLY)) { 2074 int16 roomIndex = getRoomIndex(); 2075 TR::Room::Sector *sector = level->getSector(roomIndex, pos); 2076 float floor = level->getFloor(sector, pos) - 128.0f; 2077 if (pos.y >= floor) 2078 return STATE_STOP; 2079 } 2080 2081 return STATE_FLY; 2082 } 2083 updatePositionMutant2084 virtual void updatePosition() { 2085 turn(state == STATE_RUN || state == STATE_WALK || state == STATE_FLY, (state == STATE_RUN || state == STATE_FLY) ? MUTANT_TURN_FAST : MUTANT_TURN_SLOW); 2086 2087 if (flying) 2088 lift(target->pos.y - pos.y, MUTANT_LIFT_SPEED); 2089 2090 Enemy::updatePosition(); 2091 setOverrides(true, jointChest, jointHead); 2092 lookAt(target); 2093 } 2094 }; 2095 2096 #define GIANT_MUTANT_TURN_SLOW (DEG2RAD * 90) 2097 #define GIANT_MUTANT_MIN_ANGLE (DEG2RAD * 10) 2098 #define GIANT_MUTANT_MAX_ANGLE (DEG2RAD * 45) 2099 #define GIANT_MUTANT_DAMAGE 500 2100 #define GIANT_MUTANT_DAMAGE_WALK 5 2101 #define GIANT_MUTANT_DAMAGE_FATAL 1000 2102 #define GIANT_MUTANT_DIST_ATTACK 2600 2103 #define GIANT_MUTANT_DIST_FATAL 2250 2104 #define GIANT_MUTANT_PART_DAMAGE 250 2105 2106 struct GiantMutant : Enemy { 2107 2108 enum { 2109 HIT_MASK_HAND = 0x3FF8000, 2110 HIT_MASK_HANDS = 0x3FFFFF0, 2111 }; 2112 2113 enum { 2114 ANIM_DEATH = 13, 2115 }; 2116 2117 enum { 2118 STATE_NONE, 2119 STATE_STOP, 2120 STATE_TURN_LEFT, 2121 STATE_TURN_RIGHT, 2122 STATE_ATTACK_1, 2123 STATE_ATTACK_2, 2124 STATE_ATTACK_3, 2125 STATE_WALK, 2126 STATE_BORN, 2127 STATE_FALL, 2128 STATE_UNUSED, 2129 STATE_FATAL, 2130 }; 2131 GiantMutantGiantMutant2132 GiantMutant(IGame *game, int entity) : Enemy(game, entity, 500, 341, 375.0f, 1.0f) { 2133 hitSound = TR::SND_HIT_MUTANT; 2134 stand = STAND_AIR; 2135 jointChest = -1; 2136 jointHead = -1; // 3; TODO: fix head orientation 2137 rangeHead = vec4(-0.5f, 0.5f, -0.5f, 0.5f) * PI; 2138 invertAim = true; 2139 } 2140 setSaveDataGiantMutant2141 virtual void setSaveData(const SaveEntity &data) { 2142 Enemy::setSaveData(data); 2143 if (flags.invisible) 2144 deactivate(true); 2145 } 2146 updateGiantMutant2147 void update() { 2148 bool exploded = explodeMask != 0; 2149 2150 Enemy::update(); 2151 2152 if (health <= 0.0f && !exploded && animation.index == ANIM_DEATH && flags.state == TR::Entity::asInactive) { 2153 flags.state = TR::Entity::asActive; 2154 game->playSound(TR::SND_MUTANT_DEATH, pos, Sound::PAN); 2155 explode(0xffffffff, GIANT_MUTANT_PART_DAMAGE); 2156 game->checkTrigger(this, true); 2157 } 2158 2159 setOverrides(true, jointChest, jointHead); 2160 lookAt(target); 2161 2162 if (exploded && !explodeMask) { 2163 deactivate(true); 2164 flags.invisible = true; 2165 } 2166 } 2167 getStateAirGiantMutant2168 virtual int getStateAir() { 2169 if (state == STATE_BORN) 2170 return STATE_FALL; 2171 return state; 2172 } 2173 getStateGroundGiantMutant2174 virtual int getStateGround() { 2175 if (health <= 0) 2176 return state; 2177 2178 if (!think(true)) 2179 return state; 2180 2181 if (!target || target->health <= 0.0f) 2182 return STATE_STOP; 2183 2184 int mask = collide(target); 2185 2186 if (mask) target->hit(GIANT_MUTANT_DAMAGE_WALK, this); 2187 2188 switch (state) { 2189 case STATE_FALL : 2190 animation.setState(STATE_STOP); 2191 game->shakeCamera(5.0f); 2192 break; 2193 case STATE_STOP : 2194 flags.unused = false; 2195 if (targetAngle > GIANT_MUTANT_MAX_ANGLE) return STATE_TURN_RIGHT; 2196 if (targetAngle < -GIANT_MUTANT_MAX_ANGLE) return STATE_TURN_LEFT; 2197 if (targetDist < GIANT_MUTANT_DIST_ATTACK) { 2198 if (target->health <= GIANT_MUTANT_DAMAGE) { 2199 if (targetDist < GIANT_MUTANT_DIST_FATAL) 2200 return STATE_ATTACK_3; 2201 } else 2202 return ((rand() % 2) ? STATE_ATTACK_1 : STATE_ATTACK_2); 2203 } 2204 return STATE_WALK; 2205 case STATE_WALK : 2206 if (targetDist < GIANT_MUTANT_DIST_ATTACK || 2207 targetAngle > GIANT_MUTANT_MAX_ANGLE || 2208 targetAngle < -GIANT_MUTANT_MAX_ANGLE) 2209 return STATE_STOP; 2210 break; 2211 case STATE_TURN_RIGHT : 2212 if (targetAngle < GIANT_MUTANT_MAX_ANGLE) 2213 return STATE_STOP; 2214 break; 2215 case STATE_TURN_LEFT : 2216 if (targetAngle > -GIANT_MUTANT_MAX_ANGLE) 2217 return STATE_STOP; 2218 break; 2219 case STATE_ATTACK_1 : 2220 case STATE_ATTACK_2 : 2221 if (!flags.unused && ( 2222 (state == STATE_ATTACK_1 && (mask & HIT_MASK_HAND)) || 2223 (state == STATE_ATTACK_2 && (mask & HIT_MASK_HANDS)))) { 2224 target->hit(GIANT_MUTANT_DAMAGE, this); 2225 flags.unused = true; 2226 } 2227 break; 2228 case STATE_ATTACK_3 : 2229 if ((mask & HIT_MASK_HAND) && (target->stand != STAND_HANG)) { 2230 target->hit(GIANT_MUTANT_DAMAGE_FATAL, this, TR::HIT_GIANT_MUTANT); 2231 return STATE_FATAL; 2232 } 2233 break; 2234 default : ; 2235 } 2236 2237 return state; 2238 } 2239 getStateDeathGiantMutant2240 virtual int getStateDeath() { 2241 if (animation.index != ANIM_DEATH) 2242 return animation.setAnim(ANIM_DEATH); 2243 return state; 2244 } 2245 turnGiantMutant2246 int turn(float delta, float speed) { 2247 float w = speed * Core::deltaTime; 2248 2249 updateTilt(delta, w, speed * 0.1f); 2250 2251 if (delta != 0.0f) { 2252 decrease(delta, angle.y, w); 2253 if (speed != 0.0f) { 2254 velocity = velocity.rotateY(-w); 2255 return speed < 0 ? LEFT : RIGHT; 2256 } 2257 } 2258 2259 angle.z = 0.0f; 2260 return 0; 2261 } 2262 updatePositionGiantMutant2263 virtual void updatePosition() { 2264 float angleY = 0.0f; 2265 if (target && target->health > 0.0f && fabsf(targetAngle) > GIANT_MUTANT_MIN_ANGLE) 2266 if (state == STATE_TURN_LEFT || state == STATE_TURN_RIGHT || state == STATE_WALK || state == STATE_STOP) 2267 angleY = targetAngle; 2268 2269 if (angleY != 0.0f) { 2270 turn(targetAngle, GIANT_MUTANT_TURN_SLOW); 2271 } 2272 2273 Enemy::updatePosition(); 2274 //setOverrides(true, jointChest, jointHead); 2275 //lookAt(target); 2276 } 2277 }; 2278 2279 #define CENTAUR_TURN_FAST (DEG2RAD * 120) 2280 #define CENTAUR_DIST_RUN (1024 + 512) 2281 #define CENTAUR_PART_DAMAGE 100 2282 2283 struct Centaur : Enemy { 2284 2285 enum { 2286 HIT_MASK = 0x030199, 2287 }; 2288 2289 enum { 2290 ANIM_DEATH = 8, 2291 }; 2292 2293 enum { 2294 STATE_NONE, 2295 STATE_STOP, 2296 STATE_FIRE, 2297 STATE_RUN, 2298 STATE_AIM, 2299 STATE_DEATH, 2300 STATE_IDLE, 2301 }; 2302 CentaurCentaur2303 Centaur(IGame *game, int entity) : Enemy(game, entity, 120, 341, 400.0f, 1.0f) { 2304 jointChest = 10; 2305 jointHead = 17; 2306 } 2307 setSaveDataCentaur2308 virtual void setSaveData(const SaveEntity &data) { 2309 Enemy::setSaveData(data); 2310 if (flags.invisible) 2311 deactivate(true); 2312 } 2313 getStateGroundCentaur2314 virtual int getStateGround() { 2315 if (!think(true)) 2316 return state; 2317 2318 if (nextState == state) 2319 nextState = STATE_NONE; 2320 2321 switch (state) { 2322 case STATE_STOP : 2323 if (nextState != STATE_NONE) 2324 return nextState; 2325 if (targetCanAttack && targetDist < CENTAUR_DIST_RUN) 2326 return STATE_RUN; 2327 if (targetIsVisible(MAX_SHOT_DIST)) 2328 return STATE_AIM; 2329 return STATE_RUN; 2330 case STATE_RUN : 2331 if (targetCanAttack && targetDist < CENTAUR_DIST_RUN) { 2332 nextState = STATE_IDLE; 2333 return STATE_STOP; 2334 } 2335 if (targetIsVisible(MAX_SHOT_DIST)) { 2336 nextState = STATE_AIM; 2337 return STATE_STOP; 2338 } 2339 if (rand() < 96) { 2340 nextState = STATE_IDLE; 2341 return STATE_STOP; 2342 } 2343 break; 2344 case STATE_AIM : 2345 if (nextState != STATE_NONE) 2346 return nextState; 2347 if (targetIsVisible(MAX_SHOT_DIST)) 2348 return STATE_FIRE; 2349 return STATE_STOP; 2350 case STATE_FIRE : 2351 if (nextState != STATE_NONE) 2352 break; 2353 nextState = STATE_AIM; 2354 shot(TR::Entity::CENTAUR_BULLET, 13, vec3(11.0f, 415.0f, 41.0f)); 2355 break; 2356 case STATE_IDLE : 2357 if (nextState == STATE_NONE && (collide(target) & HIT_MASK)) { 2358 bite(5, vec3(50.0f, 30.0f, 0.0f), 200); 2359 nextState = STATE_STOP; 2360 } 2361 break; 2362 } 2363 2364 return state; 2365 } 2366 getStateDeathCentaur2367 virtual int getStateDeath() { 2368 if (state == STATE_DEATH) return state; 2369 return animation.setAnim(ANIM_DEATH); 2370 } 2371 updatePositionCentaur2372 virtual void updatePosition() { 2373 turn(state == STATE_RUN, CENTAUR_TURN_FAST); 2374 2375 Enemy::updatePosition(); 2376 setOverrides(true, jointChest, jointHead); 2377 lookAt(target); 2378 } 2379 2380 virtual void deactivate(bool removeFromList = false) { 2381 if (!removeFromList) { 2382 if (!explodeMask) 2383 explode(0xffffffff, CENTAUR_PART_DAMAGE); 2384 return; 2385 } 2386 Enemy::deactivate(removeFromList); 2387 } 2388 updateCentaur2389 virtual void update() { 2390 bool exploded = explodeMask != 0; 2391 2392 Enemy::update(); 2393 2394 if (exploded && !explodeMask) { 2395 deactivate(true); 2396 flags.invisible = true; 2397 } 2398 } 2399 }; 2400 2401 2402 struct Mummy : Enemy { 2403 enum { 2404 STATE_NONE, 2405 STATE_IDLE, 2406 STATE_FALL, 2407 }; 2408 MummyMummy2409 Mummy(IGame *game, int entity) : Enemy(game, entity, 18, 341, 150.0f, 0.0f) { 2410 jointHead = 2; 2411 } 2412 updateMummy2413 virtual void update() { 2414 if (state == STATE_IDLE && (health <= 0.0f || collide(game->getLara(pos)))) { 2415 animation.setState(STATE_FALL); 2416 health = 0.0f; 2417 } 2418 Enemy::update(); 2419 } 2420 getStateGroundMummy2421 virtual int getStateGround() { 2422 if (!think(true)) 2423 return state; 2424 return state; 2425 } 2426 updatePositionMummy2427 virtual void updatePosition() { 2428 Enemy::updatePosition(); 2429 setOverrides(true, jointChest, jointHead); 2430 lookAt(target); 2431 } 2432 }; 2433 2434 2435 #define DOPPELGANGER_ROOM_CENTER (vec3(36, 0, 60) * 1024.0f) 2436 2437 struct Doppelganger : Enemy { 2438 enum { 2439 ANIM_FALL = 34, 2440 }; 2441 DoppelgangerDoppelganger2442 Doppelganger(IGame *game, int entity) : Enemy(game, entity, 1000, 341, 150.0f, 0.0f) { 2443 jointChest = 7; 2444 jointHead = 14; 2445 } 2446 2447 virtual void hit(float damage, Controller *enemy = NULL, TR::HitType hitType = TR::HIT_DEFAULT) { 2448 enemy->hit(damage * 10, this); 2449 }; 2450 updateDoppelganger2451 virtual void update() { 2452 if (!target) 2453 target = (Character*)game->getLara(pos); 2454 2455 if (stand != STAND_AIR) { 2456 pos = DOPPELGANGER_ROOM_CENTER * 2.0f - target->pos; 2457 pos.y = target->pos.y; 2458 angle = target->angle; 2459 angle.y -= PI; 2460 } 2461 2462 Enemy::updateRoom(); 2463 2464 TR::Level::FloorInfo info; 2465 getFloorInfo(getRoomIndex(), target->pos, info); 2466 float laraHeight = info.floor - target->pos.y; 2467 2468 getFloorInfo(getRoomIndex(), pos, info); 2469 float selfHeight = info.floor - pos.y; 2470 2471 if (stand != STAND_AIR && target->stand == Character::STAND_GROUND && selfHeight > 1024 && laraHeight < 256) { 2472 animation = Animation(level, target->getModel()); 2473 animation.setAnim(ANIM_FALL, 1); 2474 stand = STAND_AIR; 2475 velocity.x = velocity.y = 0.0f; 2476 } 2477 2478 if (stand == STAND_AIR) { 2479 if (selfHeight < 128.0f) { 2480 game->checkTrigger(this, true); 2481 flags.invisible = true; 2482 deactivate(true); 2483 } else { 2484 updateAnimation(true); 2485 applyGravity(velocity.y); 2486 pos += velocity * (30.0f * Core::deltaTime); 2487 } 2488 } else { 2489 animation.frameA = target->animation.frameA; 2490 animation.frameB = target->animation.frameB; 2491 animation.delta = target->animation.delta; 2492 } 2493 } 2494 getStateGroundDoppelganger2495 virtual int getStateGround() { 2496 if (!think(true)) 2497 return state; 2498 return state; 2499 } 2500 updatePositionDoppelganger2501 virtual void updatePosition() { 2502 Enemy::updatePosition(); 2503 setOverrides(true, jointChest, jointHead); 2504 lookAt(target); 2505 } 2506 }; 2507 2508 2509 struct ScionTarget : Enemy { ScionTargetScionTarget2510 ScionTarget(IGame *game, int entity) : Enemy(game, entity, 5, 0, 0, 0) {} 2511 updateScionTarget2512 virtual void update() { 2513 Controller::update(); 2514 2515 if (health <= 0.0f) { 2516 if (timer == 0.0f) { 2517 flags.invisible = true; 2518 game->checkTrigger(this, true); 2519 timer = 3.0f; 2520 } 2521 2522 if (timer > 0.0f) { 2523 int index = int(timer / 0.3f); 2524 timer -= Core::deltaTime; 2525 2526 if (index != int(timer / 0.3f)) { 2527 vec3 p = pos + vec3((randf() * 2.0f - 1.0f) * 512.0f, (randf() * 2.0f - 1.0f) * 64.0f - 500.0f, (randf() * 2.0f - 1.0f) * 512.0f); 2528 game->addEntity(TR::Entity::EXPLOSION, getRoomIndex(), p); 2529 game->shakeCamera(0.5f); 2530 } 2531 2532 if (timer < 0.0f) 2533 deactivate(true); 2534 } 2535 } 2536 } 2537 }; 2538 2539 #define HUMAN_WAIT 0.01f 2540 #define HUMAN_DIST_WALK (1024 * 3) 2541 #define HUMAN_DIST_SHOT (1024 * 7) 2542 #define HUMAN_TURN_SLOW (DEG2RAD * 90) 2543 #define HUMAN_TURN_FAST (DEG2RAD * 180) 2544 2545 struct Human : Enemy { 2546 enum { 2547 STATE_NONE, 2548 STATE_STOP, 2549 STATE_WALK, 2550 STATE_RUN, 2551 STATE_AIM, 2552 STATE_DEATH, 2553 STATE_WAIT, // == STATE_FIRE for MrT and Cowboy 2554 STATE_FIRE 2555 }; 2556 2557 int jointGun; 2558 int animDeath; 2559 HumanHuman2560 Human(IGame *game, int entity, float health) : Enemy(game, entity, health, 100, 0.0f, 1.0f), animDeath(-1) { 2561 jointGun = 0; 2562 jointChest = 7; 2563 jointHead = 8; 2564 } 2565 2566 virtual void deactivate(bool removeFromList = false) { 2567 if (health <= 0.0f) 2568 onDead(); 2569 Enemy::deactivate(removeFromList); 2570 getRoom().removeDynLight(entity); 2571 } 2572 getStateDeathHuman2573 virtual int getStateDeath() { 2574 return (animDeath == -1 || state == STATE_DEATH || state == STATE_NONE) ? STATE_DEATH : animation.setAnim(animDeath); 2575 } 2576 getStateGroundHuman2577 virtual int getStateGround() { 2578 if (!think(true)) 2579 return state; 2580 return state; 2581 } 2582 updatePositionHuman2583 virtual void updatePosition() { 2584 turn(state == STATE_RUN || state == STATE_WALK, state == STATE_RUN ? HUMAN_TURN_FAST : HUMAN_TURN_SLOW); 2585 2586 Enemy::updatePosition(); 2587 setOverrides(true, jointChest, jointHead); 2588 lookAt(target); 2589 } 2590 onDeadHuman2591 virtual void onDead() {} 2592 doShotHuman2593 bool doShot(float damage, const vec3 &muzzleOffset) { 2594 game->addMuzzleFlash(this, jointGun, muzzleOffset, -1); 2595 2596 if (targetDist < HUMAN_DIST_SHOT && randf() < ((HUMAN_DIST_SHOT - targetDist) / HUMAN_DIST_SHOT - 0.25f)) { 2597 bite(-1, vec3(0.0f), damage); 2598 game->addEntity(TR::Entity::BLOOD, target->getRoomIndex(), target->getJoint(rand() % target->getModel()->mCount).pos); 2599 game->playSound(target->stand == STAND_UNDERWATER ? TR::SND_HIT_UNDERWATER : TR::SND_HIT, target->pos, Sound::PAN); 2600 return true; 2601 } 2602 2603 int16 roomIndex = getRoomIndex(); 2604 TR::Room::Sector *sector = level->getSector(roomIndex, target->pos); 2605 float floor = level->getFloor(sector, target->pos) - 64.0f; 2606 vec3 p = vec3(target->pos.x + randf() * 512.0f - 256.0f, floor, target->pos.z + randf() * 512.0f - 256.0f); 2607 2608 target->addRicochet(p, true); 2609 return false; 2610 } 2611 }; 2612 2613 2614 #define LARSON_DAMAGE 50 2615 2616 struct Larson : Human { 2617 LarsonLarson2618 Larson(IGame *game, int entity) : Human(game, entity, 50) { 2619 animDeath = 15; 2620 jointGun = 14; 2621 } 2622 getStateGroundLarson2623 virtual int getStateGround() { 2624 if (!think(false)) 2625 return state; 2626 2627 fullChestRotation = state == STATE_FIRE || state == STATE_AIM; 2628 2629 if (nextState == state) 2630 nextState = STATE_NONE; 2631 2632 switch (state) { 2633 case STATE_STOP : 2634 if (nextState != STATE_NONE) 2635 return nextState; 2636 if (mood == MOOD_SLEEP) 2637 return randf() < HUMAN_WAIT ? STATE_WAIT : STATE_WALK; 2638 if (mood == MOOD_ESCAPE) 2639 return STATE_RUN; 2640 return STATE_WALK; 2641 case STATE_WAIT : 2642 if (mood != MOOD_SLEEP) 2643 return STATE_STOP; 2644 if (randf() < HUMAN_WAIT) { 2645 nextState = STATE_WALK; 2646 return STATE_STOP; 2647 } 2648 break; 2649 case STATE_WALK : 2650 if (mood == MOOD_SLEEP && randf() < HUMAN_WAIT) 2651 nextState = STATE_WAIT; 2652 else if (mood == MOOD_ESCAPE) 2653 nextState = STATE_RUN; 2654 else if (targetIsVisible(HUMAN_DIST_SHOT)) 2655 nextState = STATE_AIM; 2656 else if (!targetInView || targetDist > HUMAN_DIST_WALK) 2657 nextState = STATE_RUN; 2658 else 2659 break; 2660 return STATE_STOP; 2661 case STATE_RUN : 2662 if (mood == MOOD_SLEEP && randf() < HUMAN_WAIT) 2663 nextState = STATE_WAIT; 2664 else if (targetIsVisible(HUMAN_DIST_SHOT)) 2665 nextState = STATE_AIM; 2666 else if (targetInView && targetDist < HUMAN_DIST_WALK) 2667 nextState = STATE_WALK; 2668 else 2669 break; 2670 return STATE_STOP; 2671 case STATE_AIM : 2672 if (nextState != STATE_NONE) 2673 return nextState; 2674 if (targetIsVisible(HUMAN_DIST_SHOT)) 2675 return STATE_FIRE; 2676 return STATE_STOP; 2677 case STATE_FIRE : 2678 if (nextState == STATE_NONE) { 2679 doShot(LARSON_DAMAGE, vec3(-50, 0, 20)); 2680 nextState = STATE_AIM; 2681 } 2682 if (mood == MOOD_ESCAPE || target->health <= 0.0f) 2683 nextState = STATE_STOP; 2684 break; 2685 } 2686 2687 return state; 2688 } 2689 }; 2690 2691 2692 #define PIERRE_MIN_HEALTH 40 2693 #define PIERRE_DAMAGE 25 2694 2695 struct Pierre : Human { 2696 PierrePierre2697 Pierre(IGame *game, int entity) : Human(game, entity, 70) { 2698 animDeath = 12; 2699 } 2700 onDeadPierre2701 virtual void onDead() { 2702 game->addEntity(TR::Entity::MAGNUMS, getRoomIndex(), pos, 0); 2703 game->addEntity(TR::Entity::SCION_PICKUP_DROP, getRoomIndex(), pos, 0); 2704 game->addEntity(TR::Entity::KEY_ITEM_1, getRoomIndex(), pos, 0); 2705 } 2706 getStateGroundPierre2707 virtual int getStateGround() { 2708 if (!think(false)) 2709 return state; 2710 2711 if (!flags.once && health <= PIERRE_MIN_HEALTH) { 2712 health = PIERRE_MIN_HEALTH; 2713 timer += Core::deltaTime; 2714 } 2715 2716 if (timer > 0.0f && isVisible()) // time to run away! 2717 timer = 0.0f; 2718 2719 if (getRoom().flags.water) 2720 timer = 1.0f; 2721 2722 if (timer > 0.4f) { 2723 flags.invisible = true; 2724 deactivate(true); 2725 } 2726 2727 fullChestRotation = state == STATE_FIRE || state == STATE_AIM; 2728 2729 if (nextState == state) 2730 nextState = STATE_NONE; 2731 2732 switch (state) { 2733 case STATE_STOP : 2734 if (nextState != STATE_NONE) 2735 return nextState; 2736 if (mood == MOOD_SLEEP) 2737 return randf() < HUMAN_WAIT ? STATE_WAIT : STATE_WALK; 2738 if (mood == MOOD_ESCAPE) 2739 return STATE_RUN; 2740 return STATE_WALK; 2741 case STATE_WAIT : 2742 if (mood != MOOD_SLEEP) 2743 return STATE_STOP; 2744 if (randf() < HUMAN_WAIT) { 2745 nextState = STATE_WALK; 2746 return STATE_STOP; 2747 } 2748 break; 2749 case STATE_WALK : 2750 if (mood == MOOD_SLEEP && randf() < HUMAN_WAIT) 2751 nextState = STATE_WAIT; 2752 else if (mood == MOOD_ESCAPE) 2753 nextState = STATE_RUN; 2754 else if (targetIsVisible(HUMAN_DIST_SHOT)) 2755 nextState = STATE_AIM; 2756 else if (!targetInView || targetDist > HUMAN_DIST_WALK) 2757 nextState = STATE_RUN; 2758 else 2759 break; 2760 return STATE_STOP; 2761 case STATE_RUN : 2762 if (mood == MOOD_SLEEP && randf() < HUMAN_WAIT) 2763 nextState = STATE_WAIT; 2764 else if (targetIsVisible(HUMAN_DIST_SHOT)) 2765 nextState = STATE_AIM; 2766 else if (targetInView && targetDist < HUMAN_DIST_WALK) 2767 nextState = STATE_WALK; 2768 else 2769 break; 2770 return STATE_STOP; 2771 case STATE_AIM : 2772 if (nextState != STATE_NONE) 2773 return nextState; 2774 if (targetIsVisible(HUMAN_DIST_SHOT)) 2775 return STATE_FIRE; 2776 return STATE_STOP; 2777 case STATE_FIRE : 2778 if (nextState == STATE_NONE) { 2779 jointGun = 11; doShot(PIERRE_DAMAGE, vec3(60, 0, 50)); 2780 jointGun = 14; doShot(PIERRE_DAMAGE, vec3(-60, 0, 50)); 2781 nextState = STATE_AIM; 2782 } 2783 if (mood == MOOD_ESCAPE && (rand() % 2)) 2784 nextState = STATE_STOP; 2785 break; 2786 } 2787 2788 return state; 2789 } 2790 }; 2791 2792 2793 #define SKATERBOY_DIST_MIN 2560 2794 #define SKATERBOY_DIST_MAX 4096 2795 #define SKATERBOY_TURN_FAST (120 * DEG2RAD) 2796 #define SKATERBOY_DAMAGE_STAND 50.0f 2797 #define SKATERBOY_DAMAGE_MOVE 40.0f 2798 2799 struct SkaterBoy : Human { 2800 2801 enum { 2802 STATE_STOP, 2803 STATE_STAND_FIRE, 2804 STATE_MOVE, 2805 STATE_STEP, 2806 STATE_MOVE_FIRE, 2807 STATE_DEATH 2808 }; 2809 2810 Controller *board; 2811 SkaterBoySkaterBoy2812 SkaterBoy(IGame *game, int entity) : Human(game, entity, 125) { 2813 animDeath = 13; 2814 jointChest = 1; 2815 2816 board = game->addEntity(TR::Entity::ENEMY_SKATEBOARD, getRoomIndex(), pos, 0.0f); 2817 board->activate(); 2818 } 2819 onDeadSkaterBoy2820 virtual void onDead() { 2821 game->addEntity(TR::Entity::UZIS, getRoomIndex(), pos, 0); 2822 } 2823 2824 virtual void hit(float damage, Controller *enemy = NULL, TR::HitType hitType = TR::HIT_DEFAULT) { 2825 bool flag = health >= 120; 2826 Human::hit(damage, enemy, hitType); 2827 if (flag && health < 120) { 2828 game->playTrack(56, true); 2829 } 2830 }; 2831 getStateGroundSkaterBoy2832 virtual int getStateGround() { 2833 if (!think(false)) 2834 return state; 2835 2836 fullChestRotation = state == STATE_STAND_FIRE || state == STATE_MOVE_FIRE; 2837 2838 switch (state) { 2839 case STATE_STOP : 2840 flags.unused = 0; 2841 if (targetIsVisible(HUMAN_DIST_SHOT)) 2842 return STATE_STAND_FIRE; 2843 return STATE_MOVE; 2844 case STATE_MOVE : 2845 flags.unused = 0; 2846 if (rand() < 512) 2847 return STATE_STEP; 2848 if (targetIsVisible(HUMAN_DIST_SHOT)) 2849 return (mood != MOOD_ESCAPE && targetDist > SKATERBOY_DIST_MIN && targetDist < SKATERBOY_DIST_MAX) ? STATE_STOP : STATE_MOVE_FIRE; 2850 break; 2851 case STATE_STEP : 2852 if (rand() < 1024) 2853 return STATE_MOVE; 2854 break; 2855 case STATE_STAND_FIRE : 2856 case STATE_MOVE_FIRE : 2857 if (!flags.unused && targetIsVisible(HUMAN_DIST_SHOT)) { 2858 float damage = state == STATE_STAND_FIRE ? SKATERBOY_DAMAGE_STAND : SKATERBOY_DAMAGE_MOVE; 2859 jointGun = 7; doShot(damage, vec3(0, -32, 0)); 2860 jointGun = 4; doShot(damage, vec3(0, -32, 0)); 2861 flags.unused = 1; 2862 } 2863 2864 if (mood == MOOD_ESCAPE || targetDist < 1024) 2865 return STATE_RUN; 2866 break; 2867 } 2868 2869 return state; 2870 } 2871 updateSkaterBoy2872 virtual void update() { 2873 Human::update(); 2874 board->pos = pos; 2875 board->angle = angle; 2876 if (board->animation.index != animation.index) 2877 board->animation.setAnim(animation.index); 2878 board->animation.time = animation.time - Core::deltaTime; 2879 } 2880 deactivateSkaterBoy2881 virtual void deactivate(bool removeFromList) { 2882 board->deactivate(removeFromList); 2883 Human::deactivate(removeFromList); 2884 } 2885 }; 2886 2887 2888 #define COWBOY_DIST_WALK (3 * 1024) 2889 #define COWBOY_DAMAGE 70 2890 2891 struct Cowboy : Human { 2892 CowboyCowboy2893 Cowboy(IGame *game, int entity) : Human(game, entity, 150) { 2894 animDeath = 7; 2895 jointChest = 1; 2896 jointHead = 2; 2897 } 2898 onDeadCowboy2899 virtual void onDead() { 2900 game->addEntity(TR::Entity::MAGNUMS, getRoomIndex(), pos, 0); 2901 } 2902 getStateGroundCowboy2903 virtual int getStateGround() { 2904 if (!think(false)) 2905 return state; 2906 2907 fullChestRotation = state == STATE_WAIT || state == STATE_AIM; 2908 2909 if (nextState == state) 2910 nextState = STATE_NONE; 2911 2912 switch (state) { 2913 case STATE_STOP : 2914 if (nextState != STATE_NONE) 2915 return nextState; 2916 if (targetIsVisible(HUMAN_DIST_SHOT)) 2917 return STATE_AIM; 2918 if (mood == MOOD_SLEEP) 2919 return STATE_WALK; 2920 return STATE_RUN; 2921 case STATE_WALK : 2922 if (mood == MOOD_ESCAPE || !targetInView) 2923 nextState = STATE_RUN; 2924 else if (targetIsVisible(HUMAN_DIST_SHOT)) 2925 nextState = STATE_AIM; 2926 else if (targetDist > COWBOY_DIST_WALK) 2927 nextState = STATE_RUN; 2928 else 2929 break; 2930 return STATE_STOP; 2931 case STATE_RUN : 2932 if (mood == MOOD_ESCAPE || !targetInView) 2933 break; 2934 if (targetIsVisible(HUMAN_DIST_SHOT)) 2935 nextState = STATE_AIM; 2936 else if (targetDist < COWBOY_DIST_WALK && targetInView) 2937 nextState = STATE_WALK; 2938 else 2939 break; 2940 return STATE_STOP; 2941 case STATE_AIM : 2942 flags.unused = 7; 2943 if (nextState != STATE_NONE) 2944 return STATE_STOP; 2945 if (targetIsVisible(HUMAN_DIST_SHOT)) 2946 return STATE_WAIT; // STATE_FIRE 2947 return STATE_STOP; 2948 case STATE_WAIT : // STATE_FIRE 2949 if (animation.frameIndex != flags.unused && (animation.frameIndex == 0 || animation.frameIndex == 4)) { 2950 jointGun = (flags.unused == 7) ? 8 : 5; 2951 doShot(COWBOY_DAMAGE, vec3(0, -40, 40)); 2952 flags.unused = animation.frameIndex; 2953 } 2954 2955 if (mood == MOOD_ESCAPE) 2956 nextState = STATE_RUN; 2957 break; 2958 } 2959 2960 return state; 2961 } 2962 }; 2963 2964 2965 #define MRT_DIST_WALK (4 * 1024) 2966 #define MRT_DAMAGE 150 2967 2968 struct MrT : Human { 2969 MrTMrT2970 MrT(IGame *game, int entity) : Human(game, entity, 200) { 2971 animDeath = 14; 2972 jointGun = 9; 2973 jointChest = 1; 2974 jointHead = 2; 2975 state = STATE_RUN; 2976 } 2977 onDeadMrT2978 virtual void onDead() { 2979 game->addEntity(TR::Entity::SHOTGUN, getRoomIndex(), pos, 0); 2980 } 2981 getStateGroundMrT2982 virtual int getStateGround() { 2983 if (!think(false)) 2984 return state; 2985 2986 fullChestRotation = state == STATE_WAIT || state == STATE_AIM; 2987 2988 if (nextState == state) 2989 nextState = STATE_NONE; 2990 2991 switch (state) { 2992 case STATE_STOP : 2993 if (nextState != STATE_NONE) 2994 return nextState; 2995 if (targetIsVisible(HUMAN_DIST_SHOT)) 2996 return STATE_AIM; 2997 if (mood == MOOD_SLEEP) 2998 return STATE_WALK; 2999 return STATE_RUN; 3000 case STATE_WALK : 3001 if (mood == MOOD_ESCAPE || !targetInView) 3002 nextState = STATE_RUN; 3003 else if (targetIsVisible(HUMAN_DIST_SHOT)) 3004 nextState = STATE_AIM; 3005 else if (targetDist > MRT_DIST_WALK) 3006 nextState = STATE_RUN; 3007 else 3008 break; 3009 return STATE_STOP; 3010 case STATE_RUN : 3011 if (mood == MOOD_ESCAPE || !targetInView) 3012 break; 3013 if (targetIsVisible(HUMAN_DIST_SHOT)) 3014 nextState = STATE_AIM; 3015 else if (targetDist < MRT_DIST_WALK && targetInView) 3016 nextState = STATE_WALK; 3017 else 3018 break; 3019 return STATE_STOP; 3020 case STATE_AIM : 3021 flags.unused = false; 3022 if (nextState != STATE_NONE) 3023 return STATE_STOP; 3024 if (targetIsVisible(HUMAN_DIST_SHOT)) 3025 return STATE_WAIT; // STATE_FIRE 3026 return STATE_STOP; 3027 case STATE_WAIT : // STATE_FIRE 3028 if (!flags.unused) { 3029 doShot(MRT_DAMAGE, vec3(-20, -20, 300)); 3030 flags.unused = true; 3031 } 3032 if (mood == MOOD_ESCAPE) 3033 nextState = STATE_RUN; 3034 break; 3035 } 3036 3037 return state; 3038 } 3039 }; 3040 3041 #define NATLA_FIRE_ANGLE (30 * DEG2RAD) 3042 #define NATLA_FAINT_TIME 16.0f 3043 #define NATLA_HALF_HEALTH 200.0f 3044 #define NATLA_FAINT_HEALTH -8192.0f 3045 #define NATLA_LIFT_SPEED MUTANT_LIFT_SPEED 3046 3047 struct Natla : Human { 3048 3049 enum { 3050 STATE_NONE, 3051 STATE_STOP, 3052 STATE_FLY, 3053 STATE_RUN, 3054 STATE_AIM, 3055 STATE_FAINT, 3056 STATE_FIRE, 3057 STATE_FALL, 3058 STATE_STAND, 3059 STATE_DEATH, 3060 }; 3061 3062 enum { 3063 FLAG_FLY = 1, 3064 }; 3065 NatlaNatla3066 Natla(IGame *game, int entity) : Human(game, entity, 400) { 3067 jointChest = 1; 3068 jointHead = 2; 3069 } 3070 stage1Natla3071 int stage1() { 3072 flying = (flags.unused & FLAG_FLY); 3073 3074 stepHeight = 256; 3075 dropHeight = -256; 3076 3077 if (flying) { 3078 stepHeight *= 80; 3079 dropHeight *= 80; 3080 } 3081 3082 if (!think(false)) 3083 return state; 3084 3085 timer += Core::deltaTime; 3086 bool canShot = target && target->health > 0.0f && targetIsVisible(HUMAN_DIST_SHOT); 3087 3088 if (canShot && state == STATE_FLY && flying && rand() < 256) 3089 flags.unused &= ~FLAG_FLY; 3090 else if (!canShot) 3091 flags.unused |= FLAG_FLY; 3092 3093 flying = (flags.unused & FLAG_FLY); 3094 3095 if (state == nextState) 3096 nextState = STATE_NONE; 3097 3098 switch (state) { 3099 case STATE_STOP : 3100 timer = 0.0f; 3101 return flying ? STATE_FLY : STATE_AIM; 3102 case STATE_FLY : { 3103 if (timer >= 1.0f) { 3104 { //if (canShot) { ??? 3105 game->playSound(TR::SND_NATLA_SHOT, pos, Sound::PAN); 3106 shot(TR::Entity::CENTAUR_BULLET, 4, vec3(5.0f, 220.0f, 7.0f)); 3107 } 3108 timer -= 1.0f; 3109 } 3110 3111 int16 roomIndex = getRoomIndex(); 3112 TR::Room::Sector *sector = level->getSector(roomIndex, pos); 3113 float floor = level->getFloor(sector, pos) - 128.0f; 3114 3115 if (!flying && pos.y >= floor) 3116 return STATE_STOP; 3117 3118 break; 3119 } 3120 case STATE_AIM : 3121 if (nextState != STATE_NONE) 3122 return nextState; 3123 return canShot ? STATE_FIRE : STATE_STOP; 3124 case STATE_FIRE : 3125 if (nextState != STATE_NONE) 3126 return state; 3127 3128 game->playSound(TR::SND_NATLA_SHOT, pos, Sound::PAN); 3129 shot(TR::Entity::CENTAUR_BULLET, 4, vec3(5.0f, 220.0f, 7.0f)); 3130 shot(TR::Entity::CENTAUR_BULLET, 4, vec3(5.0f, 220.0f, 7.0f), 0.0f, (randf() * 2.0f - 1.0f) * (25.0f * DEG2RAD)); 3131 shot(TR::Entity::CENTAUR_BULLET, 4, vec3(5.0f, 220.0f, 7.0f), 0.0f, (randf() * 2.0f - 1.0f) * (25.0f * DEG2RAD)); 3132 3133 nextState = STATE_STOP; 3134 break; 3135 } 3136 3137 return state; 3138 } 3139 stage2Natla3140 int stage2() { 3141 stepHeight = 256; 3142 dropHeight = -256; 3143 flying = false; 3144 3145 if (!think(true)) 3146 return state; 3147 3148 timer += Core::deltaTime; 3149 bool canShot = target && target->health > 0.0f && fabsf(targetAngle) < NATLA_FIRE_ANGLE && targetIsVisible(HUMAN_DIST_SHOT); 3150 3151 switch (state) { 3152 case STATE_RUN : 3153 case STATE_STAND : 3154 if (timer >= 20.0f / 30.0f) { 3155 { // if (canShot) { ??? 3156 game->playSound(TR::SND_NATLA_SHOT, pos, Sound::PAN); 3157 shot(TR::Entity::MUTANT_BULLET, 4, vec3(5.0f, 220.0f, 7.0f)); 3158 } 3159 timer -= 20.0f / 30.0f; 3160 } 3161 return canShot ? STATE_STAND : STATE_RUN; 3162 case STATE_FLY : 3163 health = NATLA_FAINT_HEALTH; 3164 return STATE_FALL; 3165 case STATE_STOP : 3166 case STATE_AIM : 3167 case STATE_FIRE : 3168 health = NATLA_FAINT_HEALTH; 3169 return STATE_FAINT; 3170 } 3171 return state; 3172 } 3173 getStateGroundNatla3174 virtual int getStateGround() { 3175 if (health > NATLA_HALF_HEALTH) 3176 return stage1(); 3177 return stage2(); 3178 } 3179 getStateDeathNatla3180 virtual int getStateDeath() { 3181 switch (state) { 3182 case STATE_FALL : { 3183 int16 roomIndex = getRoomIndex(); 3184 TR::Room::Sector *sector = level->getSector(roomIndex, pos); 3185 float floor = level->getFloor(sector, pos); 3186 if (pos.y >= floor) { 3187 pos.y = floor; 3188 timer = 0.0f; 3189 return STATE_FAINT; 3190 } 3191 return state; 3192 } 3193 case STATE_FAINT : { 3194 timer += Core::deltaTime; 3195 if (timer >= NATLA_FAINT_TIME) { 3196 health = NATLA_HALF_HEALTH; 3197 timer = 0.0f; 3198 flags.unused = 0; 3199 game->playTrack(54, true); 3200 return STATE_STAND; 3201 } 3202 return state; 3203 } 3204 } 3205 return STATE_DEATH; 3206 } 3207 updateVelocityNatla3208 virtual void updateVelocity() { 3209 if (state == STATE_FLY) { 3210 angle.y += targetAngle; 3211 updateJoints(); 3212 angle.y -= targetAngle; 3213 } 3214 Enemy::updateVelocity(); 3215 } 3216 updatePositionNatla3217 virtual void updatePosition() { 3218 if (flying) { 3219 stand = STAND_AIR; 3220 lift(target->pos.y - pos.y, NATLA_LIFT_SPEED); 3221 } 3222 3223 turn(true, state == STATE_RUN ? HUMAN_TURN_FAST : HUMAN_TURN_SLOW); 3224 3225 Enemy::updatePosition(); 3226 setOverrides(state != STATE_FAINT, jointChest, jointHead); 3227 lookAt(target); 3228 3229 stand = STAND_GROUND; 3230 } 3231 }; 3232 3233 struct Dog : Enemy { 3234 3235 enum { 3236 ANIM_SLEEP = 5, 3237 ANIM_DEATH = 13, 3238 }; 3239 3240 enum { 3241 STATE_DEATH = 10, 3242 }; 3243 DogDog3244 Dog(IGame *game, int entity) : Enemy(game, entity, 6, 10, 0.0f, 0.0f) { 3245 jointChest = 19; 3246 jointHead = 20; 3247 animation.setAnim(ANIM_SLEEP); 3248 } 3249 getStateDeathDog3250 virtual int getStateDeath() { 3251 if (state != STATE_DEATH) 3252 return animation.setAnim(ANIM_DEATH); 3253 return state; 3254 } 3255 }; 3256 3257 #define TIGER_WALK 1120 3258 #define TIGER_ROAR 96 3259 #define TIGER_DIST_ATTACK_1 341 3260 #define TIGER_DIST_ATTACK_2 1536 3261 #define TIGER_DIST_ATTACK_3 1024 3262 #define TIGER_DAMAGE 100 3263 #define TIGER_TURN_FAST (DEG2RAD * 180) 3264 #define TIGER_TURN_SLOW (DEG2RAD * 90) 3265 3266 struct Tiger : Enemy { 3267 3268 enum { 3269 HIT_MASK = 0x7FDC000, 3270 }; 3271 3272 enum { 3273 ANIM_DEATH = 11, 3274 }; 3275 3276 enum { 3277 STATE_NONE = -1, 3278 STATE_DEATH , 3279 STATE_STOP , 3280 STATE_WALK , 3281 STATE_RUN , 3282 STATE_IDLE , 3283 STATE_ROAR , 3284 STATE_ATTACK_1 , 3285 STATE_ATTACK_2 , 3286 STATE_ATTACK_3 , 3287 }; 3288 TigerTiger3289 Tiger(IGame *game, int entity) : Enemy(game, entity, 20, 341, 200.0f, 0.25f) { 3290 dropHeight = -1024; 3291 jointChest = -1;//21; 3292 jointHead = -1;//22; 3293 nextState = STATE_NONE; 3294 } 3295 getStateGroundTiger3296 virtual int getStateGround() { 3297 if (!think(true)) 3298 return state; 3299 3300 if (nextState == state) 3301 nextState = STATE_NONE; 3302 3303 switch (state) { 3304 case STATE_STOP : 3305 if (mood == MOOD_ESCAPE) 3306 return STATE_RUN; 3307 if (mood == MOOD_SLEEP) { 3308 int r = rand(); 3309 if (r < TIGER_ROAR) return STATE_ROAR; 3310 if (r < TIGER_WALK) return STATE_WALK; 3311 return state; 3312 } 3313 if (targetInView && targetDist < TIGER_DIST_ATTACK_1) 3314 return STATE_ATTACK_1; 3315 if (targetInView && targetDist < TIGER_DIST_ATTACK_3) 3316 return STATE_ATTACK_3; 3317 if (nextState != STATE_NONE) 3318 return nextState; 3319 if (mood != MOOD_ATTACK && rand() < TIGER_ROAR) 3320 return STATE_ROAR; 3321 return STATE_RUN; 3322 case STATE_WALK : 3323 if (mood == MOOD_ATTACK || mood == MOOD_ESCAPE) 3324 return STATE_RUN; 3325 if (rand() < TIGER_ROAR) { 3326 nextState = STATE_ROAR; 3327 return STATE_STOP; 3328 } 3329 break; 3330 case STATE_RUN : { 3331 bool melee = flags.unused != 0; 3332 flags.unused = 0; 3333 3334 if (mood == MOOD_SLEEP) 3335 return STATE_STOP; 3336 if (targetInView && melee) 3337 return STATE_STOP; 3338 if (targetInView && targetDist < TIGER_DIST_ATTACK_2) { 3339 if (target->velocity.length2() < SQR(16.0f)) 3340 return STATE_STOP; 3341 else 3342 return STATE_ATTACK_2; 3343 } 3344 if (mood != MOOD_ATTACK && rand() < TIGER_ROAR) { 3345 nextState = STATE_ROAR; 3346 return STATE_STOP; 3347 } 3348 break; 3349 } 3350 case STATE_ATTACK_1 : 3351 case STATE_ATTACK_2 : 3352 case STATE_ATTACK_3 : 3353 if (flags.unused == 0 && (collide(target) & HIT_MASK)) { 3354 bite(26, vec3(19.0f, -13.0f, 3.0f), TIGER_DAMAGE); 3355 flags.unused = 1; 3356 } 3357 } 3358 return state; 3359 } 3360 getStateDeathTiger3361 virtual int getStateDeath() { 3362 if (state == STATE_DEATH) 3363 return state; 3364 return animation.setAnim(ANIM_DEATH); 3365 } 3366 updatePositionTiger3367 virtual void updatePosition() { 3368 turn(state == STATE_RUN || state == STATE_WALK || state == STATE_ROAR, state == STATE_RUN ? TIGER_TURN_FAST : TIGER_TURN_SLOW); 3369 3370 if (state == STATE_DEATH) { 3371 animation.overrideMask = 0; 3372 return; 3373 } 3374 3375 Enemy::updatePosition(); 3376 setOverrides(true, jointChest, jointHead); 3377 lookAt(target); 3378 } 3379 }; 3380 3381 3382 #define WINSTON_DIST 1536.0f 3383 #define WINSTON_TURN_SLOW (DEG2RAD * 60) 3384 #define WINSTON_FREEZE_TIME 60.0f 3385 3386 struct Winston : Enemy { 3387 3388 enum { 3389 STATE_NONE , 3390 STATE_STOP , 3391 STATE_WALK , 3392 }; 3393 3394 Texture *environment; 3395 WinstonWinston3396 Winston(IGame *game, int entity) : Enemy(game, entity, 20, 341, 200.0f, 0.25f), environment(NULL) { 3397 dropHeight = -1024; 3398 jointChest = 11; 3399 jointHead = 25; 3400 nextState = STATE_NONE; 3401 lookAtSpeed = 1.0f; 3402 timer = 0.0f; 3403 } 3404 ~WinstonWinston3405 virtual ~Winston() { 3406 delete environment; 3407 } 3408 getStateGroundWinston3409 virtual int getStateGround() { 3410 if (getRoomIndex() == 94) { 3411 int doorIndex = (level->version & TR::VER_TR2) ? 38 : 68; 3412 3413 Controller *door = (Controller*)game->getLevel()->entities[doorIndex].controller; 3414 if (!door->isActive()) { 3415 if (timer > WINSTON_FREEZE_TIME) { 3416 flags.unused |= 4; 3417 if (!environment) { 3418 bakeEnvironment(environment); 3419 } 3420 } else { 3421 timer += Core::deltaTime; 3422 } 3423 return STATE_STOP; 3424 } else { 3425 if (!(flags.unused & 4)) { 3426 timer = 0.0f; 3427 } 3428 } 3429 } 3430 3431 if (flags.unused & 4) { 3432 timer -= Core::deltaTime; 3433 if (timer < 0.0f) { 3434 timer = 0.0f; 3435 flags.unused &= ~4; 3436 } 3437 return STATE_STOP; 3438 } 3439 3440 if (!think(false)) 3441 return state; 3442 3443 if (nextState == state) 3444 nextState = STATE_NONE; 3445 3446 if (nextState != STATE_NONE) 3447 return nextState; 3448 3449 switch (state) { 3450 case STATE_STOP : 3451 if ((targetDist > WINSTON_DIST || !targetInView) && nextState != STATE_WALK) { 3452 nextState = STATE_WALK; 3453 game->playSound(TR::SND_WINSTON_WALK, pos, Sound::PAN); 3454 } 3455 case STATE_WALK : 3456 if (targetDist < WINSTON_DIST) { 3457 if (targetInView) { 3458 nextState = STATE_STOP; 3459 flags.unused &= ~1; 3460 } else if (!(flags.unused & 1)) { 3461 game->playSound(TR::SND_WINSTON_SCARED, pos, Sound::PAN); 3462 game->playSound(TR::SND_WINSTON_TRAY, pos, Sound::PAN); 3463 flags.unused |= 1; 3464 } 3465 } 3466 } 3467 3468 bool touch = collide(target) != 0; 3469 bool push = (flags.unused & 2) != 0; 3470 3471 if (!push && touch) { 3472 game->playSound(TR::SND_WINSTON_PUSH, pos, Sound::PAN); 3473 game->playSound(TR::SND_WINSTON_TRAY, pos, Sound::PAN); 3474 flags.unused |= 2; 3475 } 3476 3477 if (push && !touch) { 3478 flags.unused &= ~2; 3479 } 3480 3481 if (rand() < 0x100) { 3482 game->playSound(TR::SND_WINSTON_TRAY, pos, Sound::PAN); 3483 } 3484 3485 return state; 3486 } 3487 updatePositionWinston3488 virtual void updatePosition() { 3489 if (flags.unused & 4) { 3490 animation.time = 0.0f; 3491 animation.updateInfo(); 3492 return; 3493 } 3494 3495 turn(state == STATE_WALK, WINSTON_TURN_SLOW); 3496 angle.z = 0.0f; 3497 3498 Enemy::updatePosition(); 3499 setOverrides(true, jointChest, jointHead); 3500 lookAt(target); 3501 } 3502 renderWinston3503 virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { 3504 if (environment && (flags.unused & 4)) { 3505 game->setRoomParams(getRoomIndex(), Shader::MIRROR, 1.5f, 2.0f, 2.5f, 1.0f, false); 3506 GAPI::Texture *dtex = Core::active.textures[sDiffuse]; 3507 environment->bind(sDiffuse); 3508 Controller::render(frustum, mesh, type, caustics); 3509 if (dtex) dtex->bind(sDiffuse); 3510 } else { 3511 Enemy::render(frustum, mesh, type, caustics); 3512 } 3513 } 3514 }; 3515 3516 #endif