1 #ifndef H_LARA 2 #define H_LARA 3 /*****************************************/ 4 /* Desine sperare qui hic intras */ 5 /*****************************************/ 6 #include "character.h" 7 #include "objects.h" 8 #include "sprite.h" 9 #include "enemy.h" 10 #include "inventory.h" 11 12 // TODO: slide to slide in WALL 13 // TODO: static sounds in LEVEL3A 14 // TODO: fix enemy head rotation glitches 15 16 #define TURN_FAST PI 17 #define TURN_FAST_BACK PI * 3.0f / 4.0f 18 #define TURN_NORMAL PI / 2.0f 19 #define TURN_SLOW PI / 3.0f 20 #define TURN_WATER_FAST (DEG2RAD * 150.0f) 21 #define TURN_WATER_SLOW (DEG2RAD * 60.0f) 22 #define TURN_WALL_Y (DEG2RAD * 150.0f) 23 #define TURN_WALL_X (DEG2RAD * 60.0f) 24 #define TURN_WALL_X_CLAMP (DEG2RAD * 35.0f) 25 26 #define LARA_TILT_SPEED (DEG2RAD * 37.5f) 27 #define LARA_TILT_MAX (DEG2RAD * 10.0f) 28 29 #define LARA_MAX_HEALTH 1000.0f 30 #define LARA_MAX_OXYGEN 60.0f 31 32 #define LARA_HANG_OFFSET 724 33 #define LARA_HEIGHT 762 34 #define LARA_HEIGHT_WATER 400 35 #define LARA_RADIUS 100.0f 36 #define LARA_RADIUS_WATER 300.0f 37 38 #define LARA_WATER_ACCEL 2.0f 39 #define LARA_SURF_SPEED 15.0f 40 #define LARA_SWIM_SPEED 50.0f 41 #define LARA_SWIM_FRICTION 1.0f 42 43 #define LARA_WADE_DEPTH 384.0f 44 #define LARA_WADE_MAX_DEPTH 730.0f 45 #define LARA_SWIM_MIN_DEPTH 512.0f 46 47 #define LARA_MIN_SPECULAR 0.03f 48 #define LARA_WET_SPECULAR 0.5f 49 #define LARA_WET_TIMER (LARA_WET_SPECULAR / 16.0f) // 4 sec 50 51 #define LARA_DAMAGE_TIME (40.0f / 30.0f) 52 53 #define PICKUP_FRAME_GROUND 40 54 #define PICKUP_FRAME_UNDERWATER 18 55 #define PUZZLE_FRAME 80 56 #define KEY_FRAME 110 57 58 #define MAX_TRIGGER_ACTIONS 64 59 60 #define DESCENT_SPEED 2048.0f 61 #define TARGET_MAX_DIST (8.0f * 1024.0f) 62 63 #define UNITS_PER_METER 445.0f 64 65 #define LARA_VIBRATE_HIT_TIME 0.2f 66 67 #define COLLIDE_MAX_RANGE (1024.0f * 4.0f) 68 69 struct Lara : Character { 70 71 // http://www.tombraiderforums.com/showthread.php?t=148859 72 enum { 73 ANIM_RUN = 0, 74 75 ANIM_STAND_LEFT = 2, 76 ANIM_STAND_RIGHT = 3, 77 78 ANIM_RUN_START = 6, 79 80 ANIM_STAND = 11, 81 82 ANIM_LANDING = 24, 83 84 ANIM_CLIMB_JUMP = 26, 85 86 ANIM_FALL_HANG = 28, 87 88 ANIM_SMASH_JUMP = 32, 89 90 ANIM_FALL_FORTH = 34, 91 92 ANIM_BACK = 41, 93 ANIM_CLIMB_3 = 42, 94 95 ANIM_CLIMB_2 = 50, 96 97 ANIM_SMASH_RUN_LEFT = 53, 98 ANIM_SMASH_RUN_RIGHT = 54, 99 ANIM_RUN_ASCEND_LEFT = 55, 100 ANIM_RUN_ASCEND_RIGHT = 56, 101 ANIM_WALK_ASCEND_LEFT = 57, 102 ANIM_WALK_ASCEND_RIGHT = 58, 103 ANIM_WALK_DESCEND_RIGHT = 59, 104 ANIM_WALK_DESCEND_LEFT = 60, 105 ANIM_BACK_DESCEND_LEFT = 61, 106 ANIM_BACK_DESCEND_RIGHT = 62, 107 108 ANIM_SLIDE_FORTH = 70, 109 110 ANIM_FALL_BACK = 93, 111 112 ANIM_HANG = 96, 113 114 ANIM_STAND_NORMAL = 103, 115 116 ANIM_SLIDE_BACK = 105, 117 118 ANIM_UNDERWATER = 108, 119 120 ANIM_WATER_FALL = 112, 121 ANIM_TO_ONWATER = 114, 122 ANIM_ONWATER_SWIM_F = 116, 123 ANIM_ONWATER_SWIM_B = 141, 124 ANIM_ONWATER_SWIM_L = 143, 125 ANIM_ONWATER_SWIM_R = 144, 126 127 ANIM_TO_UNDERWATER = 119, 128 ANIM_HIT_FRONT = 125, 129 ANIM_HIT_BACK = 126, 130 ANIM_HIT_LEFT = 127, 131 ANIM_HIT_RIGHT = 128, 132 133 ANIM_DEATH_BOULDER = 139, 134 135 ANIM_STAND_ROLL_BEGIN = 146, 136 ANIM_STAND_ROLL_END = 147, 137 138 ANIM_DEATH_SPIKES = 149, 139 ANIM_HANG_SWING = 150, 140 141 ANIM_WADE_SWIM = 176, 142 ANIM_WADE = 177, 143 ANIM_WADE_RUN_LEFT = 178, 144 ANIM_WADE_RUN_RIGHT = 179, 145 ANIM_WADE_STAND = 186, 146 ANIM_WADE_ASCEND = 190, 147 ANIM_WATER_OUT = 191, 148 ANIM_SWIM_STAND = 192, 149 ANIM_SURF_STAND = 193, 150 151 ANIM_SWITCH_BIG_DOWN = 195, 152 ANIM_SWITCH_BIG_UP = 196, 153 ANIM_PUSH_BUTTON = 197, 154 155 ANIM_ROLL_WATER = 203, 156 }; 157 158 // http://www.tombraiderforums.com/showthread.php?t=211681 159 enum { 160 STATE_WALK, 161 STATE_RUN, 162 STATE_STOP, 163 STATE_FORWARD_JUMP, 164 STATE_UNUSED_0, 165 STATE_FAST_BACK, 166 STATE_TURN_RIGHT, 167 STATE_TURN_LEFT, 168 STATE_DEATH, 169 STATE_FALL, 170 STATE_HANG, 171 STATE_REACH, 172 STATE_SPLAT, 173 STATE_TREAD, 174 STATE_FAST_TURN_14, 175 STATE_COMPRESS, 176 STATE_BACK, 177 STATE_SWIM, 178 STATE_GLIDE, 179 STATE_HANG_UP, 180 STATE_FAST_TURN, 181 STATE_STEP_RIGHT, 182 STATE_STEP_LEFT, 183 STATE_ROLL_END, 184 STATE_SLIDE, 185 STATE_BACK_JUMP, 186 STATE_RIGHT_JUMP, 187 STATE_LEFT_JUMP, 188 STATE_UP_JUMP, 189 STATE_FALL_BACK, 190 STATE_HANG_LEFT, 191 STATE_HANG_RIGHT, 192 STATE_SLIDE_BACK, 193 STATE_SURF_TREAD, 194 STATE_SURF_SWIM, 195 STATE_DIVE, 196 STATE_PUSH_BLOCK, 197 STATE_PULL_BLOCK, 198 STATE_PUSH_PULL_READY, 199 STATE_PICK_UP, 200 STATE_SWITCH_DOWN, 201 STATE_SWITCH_UP, 202 STATE_USE_KEY, 203 STATE_USE_PUZZLE, 204 STATE_UNDERWATER_DEATH, 205 STATE_ROLL_START, 206 STATE_SPECIAL, 207 STATE_SURF_BACK, 208 STATE_SURF_LEFT, 209 STATE_SURF_RIGHT, 210 STATE_MIDAS_USE, 211 STATE_MIDAS_DEATH, 212 STATE_SWAN_DIVE, 213 STATE_FAST_DIVE, 214 STATE_HANDSTAND, 215 STATE_WATER_OUT, 216 STATE_CLIMB_START, 217 STATE_CLIMB_UP, 218 STATE_CLIMB_LEFT, 219 STATE_CLIMB_END, 220 STATE_CLIMB_RIGHT, 221 STATE_CLIMB_DOWN, 222 STATE_UNUSED_1, 223 STATE_UNUSED_2, 224 STATE_UNUSED_3, 225 STATE_WADE, 226 STATE_ROLL_WATER, 227 STATE_PICKUP_FLARE, 228 STATE_ROLL_AIR, 229 STATE_UNUSED_5, 230 STATE_DEATH_SLIDE, 231 232 STATE_MAX }; 233 234 #define LARA_RGUN_JOINT 10 235 #define LARA_LGUN_JOINT 13 236 #define LARA_RGUN_OFFSET vec3(-10, -50, 0) 237 #define LARA_LGUN_OFFSET vec3( 10, -50, 0) 238 239 enum { 240 JOINT_HIPS = 0, 241 JOINT_LEG_L1, 242 JOINT_LEG_L2, 243 JOINT_LEG_L3, 244 JOINT_LEG_R1, 245 JOINT_LEG_R2, 246 JOINT_LEG_R3, 247 JOINT_CHEST, 248 JOINT_ARM_R1, 249 JOINT_ARM_R2, 250 JOINT_ARM_R3, 251 JOINT_ARM_L1, 252 JOINT_ARM_L2, 253 JOINT_ARM_L3, 254 JOINT_HEAD, 255 JOINT_MAX 256 }; 257 258 enum { 259 JOINT_MASK_HIPS = 1 << JOINT_HIPS, 260 JOINT_MASK_LEG_L1 = 1 << JOINT_LEG_L1, 261 JOINT_MASK_LEG_L2 = 1 << JOINT_LEG_L2, 262 JOINT_MASK_LEG_L3 = 1 << JOINT_LEG_L3, 263 JOINT_MASK_LEG_R1 = 1 << JOINT_LEG_R1, 264 JOINT_MASK_LEG_R2 = 1 << JOINT_LEG_R2, 265 JOINT_MASK_LEG_R3 = 1 << JOINT_LEG_R3, 266 JOINT_MASK_CHEST = 1 << JOINT_CHEST, 267 JOINT_MASK_ARM_R1 = 1 << JOINT_ARM_R1, 268 JOINT_MASK_ARM_R2 = 1 << JOINT_ARM_R2, 269 JOINT_MASK_ARM_R3 = 1 << JOINT_ARM_R3, 270 JOINT_MASK_ARM_L1 = 1 << JOINT_ARM_L1, 271 JOINT_MASK_ARM_L2 = 1 << JOINT_ARM_L2, 272 JOINT_MASK_ARM_L3 = 1 << JOINT_ARM_L3, 273 JOINT_MASK_HEAD = 1 << JOINT_HEAD, 274 JOINT_MASK_ARM_L = JOINT_MASK_ARM_L1 | JOINT_MASK_ARM_L2 | JOINT_MASK_ARM_L3, 275 JOINT_MASK_ARM_R = JOINT_MASK_ARM_R1 | JOINT_MASK_ARM_R2 | JOINT_MASK_ARM_R3, 276 JOINT_MASK_LEG_L = JOINT_MASK_LEG_L1 | JOINT_MASK_LEG_L2 | JOINT_MASK_LEG_L3, 277 JOINT_MASK_LEG_R = JOINT_MASK_LEG_R1 | JOINT_MASK_LEG_R2 | JOINT_MASK_LEG_R3, 278 JOINT_MASK_UPPER = JOINT_MASK_CHEST | JOINT_MASK_ARM_L | JOINT_MASK_ARM_R, // without head 279 JOINT_MASK_LOWER = JOINT_MASK_HIPS | JOINT_MASK_LEG_L | JOINT_MASK_LEG_R, 280 JOINT_MASK_BRAID = JOINT_MASK_HEAD | JOINT_MASK_CHEST | JOINT_MASK_ARM_L1 | JOINT_MASK_ARM_L2 | JOINT_MASK_ARM_R1 | JOINT_MASK_ARM_R2, 281 }; 282 283 struct Weapon { 284 enum State { IS_HIDDEN, IS_ARMED, IS_FIRING }; 285 struct Anim { 286 enum Type { NONE, PREPARE, UNHOLSTER, HOLSTER, HOLD, AIM, FIRE }; 287 }; 288 }; 289 290 TR::Entity::Type wpnCurrent; 291 TR::Entity::Type wpnNext; 292 Weapon::State wpnState; 293 294 struct Arm { 295 Controller *tracking; // tracking target (main target) 296 Controller *target; // target for shooting 297 quat rot, rotAbs; 298 299 Weapon::Anim::Type anim; 300 Animation animation; 301 ArmLara::Arm302 Arm() : tracking(NULL), target(NULL) {} 303 } arms[2]; 304 305 TR::Entity::Type itemHolster; 306 TR::Entity::Type usedItem; 307 int pickupListCount; 308 Controller *pickupList[32]; 309 KeyHole *keyHole; 310 Controller *keyItem; 311 Lightning *lightning; 312 Texture *environment; 313 vec2 rotFactor; 314 315 float oxygen; 316 float damageTime; 317 float hitTime; 318 int hitDir; 319 vec3 collisionOffset; 320 vec3 flowVelocity; 321 float statsDistDelta; 322 323 Camera *camera; 324 325 float hitTimer; 326 327 bool dozy; 328 bool canJump; 329 330 int32 networkInput; 331 332 #ifdef _DEBUG 333 //uint16 *dbgBoxes; 334 //int dbgBoxesCount; 335 #endif 336 struct Braid { 337 Lara *lara; 338 vec3 offset; 339 340 Basis *basis; 341 struct Joint { 342 vec3 posPrev, pos; 343 float length; 344 } *joints; 345 int jointsCount; 346 float time; 347 BraidLara::Braid348 Braid(Lara *lara, const vec3 &offset) : lara(lara), offset(offset), time(0.0f) { 349 TR::Level *level = lara->level; 350 TR::Model *model = getModel(); 351 jointsCount = model->mCount + 1; 352 joints = new Joint[jointsCount]; 353 basis = new Basis[jointsCount - 1]; 354 355 Basis basis = getBasis(); 356 basis.translate(offset); 357 358 TR::Node *node = (int)model->node < level->nodesDataSize ? (TR::Node*)&level->nodesData[model->node] : NULL; 359 for (int i = 0; i < jointsCount - 1; i++) { 360 TR::Node &t = node[min(i, model->mCount - 2)]; 361 joints[i].posPrev = joints[i].pos = basis.pos; 362 joints[i].length = float(t.z); 363 basis.translate(vec3(0.0f, 0.0f, -joints[i].length)); 364 } 365 joints[jointsCount - 1].posPrev = joints[jointsCount - 1].pos = basis.pos; 366 joints[jointsCount - 1].length = 1.0f; 367 } 368 ~BraidLara::Braid369 ~Braid() { 370 delete[] joints; 371 delete[] basis; 372 } 373 getModelLara::Braid374 TR::Model* getModel() { 375 return &lara->level->models[lara->level->extra.braid]; 376 } 377 getBasisLara::Braid378 Basis getBasis() { 379 return lara->getJoint(lara->jointHead); 380 } 381 getPosLara::Braid382 vec3 getPos() { 383 return getBasis() * offset; 384 } 385 integrateLara::Braid386 void integrate() { 387 float TIMESTEP = Core::deltaTime; 388 float ACCEL = 16.0f * GRAVITY * 30.0f * TIMESTEP * TIMESTEP; 389 float DAMPING = 1.5f; 390 391 if (lara->stand == STAND_UNDERWATER) { 392 ACCEL *= 0.5f; 393 DAMPING = 4.0f; 394 } 395 396 DAMPING = 1.0f / (1.0f + DAMPING * TIMESTEP); // Pade approximation 397 398 for (int i = 1; i < jointsCount; i++) { 399 Joint &j = joints[i]; 400 vec3 delta = j.pos - j.posPrev; 401 delta = delta.normal() * (min(delta.length(), 2048.0f * Core::deltaTime) * DAMPING); // speed limit 402 j.posPrev = j.pos; 403 j.pos += delta; 404 if ((lara->stand == STAND_WADE || lara->stand == STAND_ONWATER || lara->stand == STAND_UNDERWATER) && (j.pos.y > lara->waterLevel)) 405 j.pos.y -= ACCEL; 406 else 407 j.pos.y += ACCEL; 408 } 409 } 410 collideLara::Braid411 void collide() { 412 TR::Level *level = lara->level; 413 const TR::Model *model = lara->getModel(); 414 415 TR::Level::FloorInfo info; 416 lara->getFloorInfo(lara->getRoomIndex(), lara->getViewPoint(), info); 417 418 for (int j = 1; j < jointsCount; j++) 419 if (joints[j].pos.y > info.floor) 420 joints[j].pos.y = info.floor; 421 422 #define BRAID_RADIUS 0.0f 423 424 lara->updateJoints(); 425 426 for (int i = 0; i < model->mCount; i++) { 427 if (!(JOINT_MASK_BRAID & (1 << i))) continue; 428 429 int offset = level->meshOffsets[model->mStart + i]; 430 TR::Mesh *mesh = (TR::Mesh*)&level->meshes[offset]; 431 432 vec3 center = lara->joints[i] * mesh->center; 433 float radiusSq = mesh->radius + BRAID_RADIUS; 434 radiusSq *= radiusSq; 435 436 for (int j = 1; j < jointsCount; j++) { 437 vec3 dir = joints[j].pos - center; 438 float len = dir.length2() + EPS; 439 if (len < radiusSq) { 440 len = sqrtf(len); 441 dir *= (mesh->radius + BRAID_RADIUS- len) / len; 442 joints[j].pos += dir * 0.9f; 443 } 444 } 445 } 446 447 #undef BRAID_RADIUS 448 } 449 solveLara::Braid450 void solve() { 451 for (int i = 0; i < jointsCount - 1; i++) { 452 Joint &a = joints[i]; 453 Joint &b = joints[i + 1]; 454 455 vec3 dir = b.pos - a.pos; 456 float len = dir.length() + EPS; 457 dir *= 1.0f / len; 458 459 float d = a.length - len; 460 461 if (i > 0) { 462 dir *= d * (0.5f * 1.0f); 463 a.pos -= dir; 464 b.pos += dir; 465 } else 466 b.pos += dir * (d * 1.0f); 467 } 468 } 469 updateLara::Braid470 void update() { 471 joints[0].pos = getPos(); 472 integrate(); // Verlet integration step 473 collide(); // check collision with Lara's mesh 474 for (int i = 0; i < jointsCount; i++) // solve connections (springs) 475 solve(); 476 477 vec3 headDir = getBasis().rot * vec3(0.0f, 0.0f, -1.0f); 478 479 for (int i = 0; i < jointsCount - 1; i++) { 480 vec3 d = (joints[i + 1].pos - joints[i].pos).normal(); 481 vec3 r = d.cross(headDir).normal(); 482 vec3 u = d.cross(r).normal(); 483 484 mat4 m; 485 m.up() = vec4(u, 0.0f); 486 m.dir() = vec4(d, 0.0f); 487 m.right() = vec4(r, 0.0f); 488 m.offset() = vec4(0.0f, 0.0f, 0.0f, 1.0f); 489 490 basis[i].identity(); 491 basis[i].translate(joints[i].pos); 492 basis[i].rotate(m.getRot()); 493 } 494 } 495 renderLara::Braid496 void render(MeshBuilder *mesh) { 497 Core::setBasis(basis, jointsCount - 1); 498 mesh->renderModel(lara->level->extra.braid); 499 } 500 501 } *braid[2]; 502 LaraLara503 Lara(IGame *game, int entity) : Character(game, entity, LARA_MAX_HEALTH), wpnCurrent(TR::Entity::NONE), wpnNext(TR::Entity::NONE) { 504 camera = new Camera(game, this); 505 506 braid[0] = braid[1] = NULL; 507 508 itemHolster = TR::Entity::NONE; 509 hitTimer = 0.0f; 510 networkInput = -1; 511 512 dozy = false; 513 canJump = true; 514 515 if (level->extra.laraSkin > -1) 516 level->entities[entity].modelIndex = level->extra.laraSkin + 1; 517 518 jointChest = level->isCutsceneLevel() ? 0 : JOINT_CHEST; 519 jointHead = JOINT_HEAD; 520 rangeChest = vec4(-0.50f, 0.50f, -0.95f, 0.95f) * PI; 521 rangeHead = vec4(-0.30f, 0.30f, -0.55f, 0.55f) * PI; 522 523 statsDistDelta = 0.0f; 524 525 oxygen = LARA_MAX_OXYGEN; 526 hitDir = -1; 527 damageTime = LARA_DAMAGE_TIME; 528 hitTime = 0.0f; 529 530 keyHole = NULL; 531 keyItem = NULL; 532 lightning = NULL; 533 environment = NULL; 534 535 flags.active = 1; 536 initMeshOverrides(); 537 538 if (level->isHome()) { 539 if (level->version & TR::VER_TR1) 540 meshSwap(1, TR::MODEL_LARA_SPEC, JOINT_MASK_UPPER | JOINT_MASK_LOWER); 541 } else { 542 int *wpnAmmo = game->invCount(TR::Entity::PISTOLS); 543 if (wpnAmmo && *wpnAmmo > 0) 544 wpnSet(TR::Entity::PISTOLS); 545 } 546 547 for (int i = 0; i < 2; i++) { 548 arms[i].rot = quat(0, 0, 0, 1); 549 arms[i].rotAbs = quat(0, 0, 0, 1); 550 } 551 552 if (level->extra.braid > -1) { 553 vec3 offset(0.0f); 554 switch (level->version & TR::VER_VERSION) { 555 case TR::VER_TR1 : 556 //braid[0] = new Braid(this, vec3(-4.0f, 24.0f, -48.0f)); // it's just ugly :) 557 break; 558 case TR::VER_TR2 : 559 case TR::VER_TR3 : 560 braid[0] = new Braid(this, vec3(0.0f, -23.0f, -55.0f)); 561 break; 562 case TR::VER_TR4 : 563 if (isYoung()) { 564 braid[0] = new Braid(this, vec3(-32.0f, -48.0f, -32.0f)); 565 braid[1] = new Braid(this, vec3( 32.0f, -48.0f, -32.0f)); 566 } else { 567 braid[0] = new Braid(this, vec3(0.0f, -23.0f, -32.0f)); 568 } 569 break; 570 } 571 } 572 573 // TR1 574 //reset(14, vec3(40448, 3584, 60928), PI * 0.5f, STAND_ONWATER); // gym (pool) 575 //reset(0, vec3(74858, 3072, 20795), 0); // level 1 (dart) 576 //reset(14, vec3(20215, 6656, 52942), PI); // level 1 (bridge) 577 //reset(20, vec3(8952, 3840, 68071), PI); // level 1 (crystal) 578 //reset(26, vec3(24475, 6912, 83505), 90 * DEG2RAD); // level 1 (switch timer) 579 //reset(33, vec3(48229, 4608, 78420), 270 * DEG2RAD); // level 1 (end) 580 //reset(9, vec3(63008, 0, 37787), 0); // level 2 (switch) 581 //reset(15, vec3(70067, -256, 29104), -0.68f); // level 2 (pool) 582 //reset(5, vec3(55398, 0, 29246), -PI * 0.5f); // level 2 (key hole) 583 //reset(71, vec3(12473, -768, 30208), -PI * 0.5f); // level 2 (puzzle hole) 584 //reset(26, vec3(71980, 1546, 19000), 270 * DEG2RAD); // level 2 (underwater switch) 585 //reset(61, vec3(27221, -1024, 29205), -PI * 0.5f); // level 2 (blade) 586 //reset(43, vec3(31400, -2560, 25200), PI); // level 2 (reach) 587 //reset(16, vec3(60907, 0, 39642), PI * 3 / 2); // level 2 (hang & climb) 588 //reset(19, vec3(60843, 1024, 30557), PI); // level 2 (block) 589 //reset(1, vec3(62630, -1280, 19633), 0); // level 2 (dark medikit) 590 //reset(7, vec3(64108, -512, 16514), -PI * 0.5f); // level 2 (bat trigger) 591 //reset(15, vec3(70082, -512, 26935), PI * 0.5f); // level 2 (bear) 592 //reset(63, vec3(31390, -2048, 33472), 0.0f); // level 2 (trap floor) 593 //reset(61, vec3(21987, -1024, 29144), PI * 3.0f * 0.5f); // level 2 (trap door) 594 //reset(51, vec3(41015, 3584, 34494), -PI); // level 3a (t-rex) 595 //reset(5, vec3(38643, -3072, 92370), PI * 0.5f); // level 3a (gears) 596 //reset(43, vec3(64037, 6656, 48229), PI); // level 3b (movingblock) 597 //reset(27, vec3(72372, 8704, 46547), PI * 0.5f); // level 3b (spikes) 598 //reset(5, vec3(73394, 3840, 60758), 0); // level 3b (scion) 599 //reset(20, vec3(57724, 6656, 61941), 90 * DEG2RAD); // level 3b (boulder) 600 //reset(18, vec3(34914, 11008, 41315), 90 * DEG2RAD); // level 4 (main hall) 601 //reset(19, vec3(33368, 19968, 45643), 270 * DEG2RAD); // level 4 (damocles) 602 //reset(24, vec3(45609, 18176, 41500), 90 * DEG2RAD); // level 4 (thor) 603 //reset(19, vec3(41418, -3707, 58863), 270 * DEG2RAD); // level 5 (triangle) 604 //reset(21, vec3(24106, -4352, 52089), 0); // level 6 (flame traps) 605 //reset(73, vec3(73372, 122, 51687), PI * 0.5f); // level 6 (midas hand) 606 //reset(20, vec3(25088, -5120, 17593), PI); // level 6 (puzzle slots) 607 //reset(64, vec3(36839, -2560, 48769), 270 * DEG2RAD); // level 6 (flipmap effect) 608 //reset(99, vec3(45562, -3328, 63366), 225 * DEG2RAD); // level 7a (flipmap) 609 //reset(77, vec3(36943, -4096, 62821), 270 * DEG2RAD); // level 7b (heavy trigger) 610 //reset(90, vec3(19438, 3840, 78341), 90 * DEG2RAD); // level 7b (statues) 611 //reset(90, vec3(29000, 3840 - 512, 78341), 90 * DEG2RAD); // level 7b (statues) 612 //reset(57, vec3(54844, -3328, 53145), 0); // level 8b (bridge switch) 613 //reset(12, vec3(34236, -2415, 14974), 0); // level 8b (sphinx) 614 //reset(0, vec3(40913, -1012, 42252), PI); // level 8c 615 //reset(56, vec3(18541, 512, 52869), PI * 0.5f); // level 8c 616 //reset(30, vec3(69689, -8448, 34922), 330 * DEG2RAD); // Level 10a (cabin) 617 //reset(27, vec3(52631, -4352, 57893), 270 * DEG2RAD); // Level 10a (TNT / Cowboy) 618 //reset(57, vec3(71081, 5632, 73042), 0); // Level 10a (Skaterboy) 619 //reset(68, vec3(52458, -9984, 93724), 270 * DEG2RAD); // Level 10a (MrT) 620 //reset(44, vec3(75803, -11008, 21097), 90 * DEG2RAD); // Level 10a (boat) 621 //reset(47, vec3(50546, -13056, 53783), 270 * DEG2RAD); // Level 10b (trap door slope) 622 //reset(59, vec3(42907, -13056, 63012), 270 * DEG2RAD); // Level 10b (doppelganger) 623 //reset(53, vec3(39617, -18385, 48950), 180 * DEG2RAD); // Level 10b (centaur) 624 //reset(50, vec3(52122, -18688, 47313), 150 * DEG2RAD); // Level 10b (scion holder pickup) 625 //reset(50, vec3(53703, -18688, 13769), PI); // Level 10c (scion holder) 626 //reset(19, vec3(35364, -512, 40199), PI * 0.5f); // Level 10c (lava flow) 627 //reset(9, vec3(69074, -14592, 25192), 0); // Level 10c (trap slam) 628 //reset(21, vec3(47668, -10752, 32163), 0); // Level 10c (lava emitter) 629 //reset(29, vec3(61586, -439, 51734), PI * 0.5f); // Level 10c (precise falling) 630 //reset(33, vec3(64641, 9578, 61861), 0); // Level 10c (Natla) 631 //reset(10, vec3(90443, 11264 - 256, 114614), PI, STAND_ONWATER); // villa mortal 2 632 // TR2 633 //reset(36, vec3(64190, 5632, 35743), 75 * DEG2RAD); // WALL (wade) 634 //reset(19, vec3(28353, 2560, 58587), 150 * DEG2RAD); // ASSAULT (wade) 635 636 //dbgBoxes = NULL; 637 638 if (!level->isCutsceneLevel()) { 639 if (getRoom().flags.water) { 640 stand = STAND_UNDERWATER; 641 animation.setAnim(ANIM_UNDERWATER); 642 } else 643 animation.setAnim(ANIM_STAND); 644 } 645 } 646 ~LaraLara647 virtual ~Lara() { 648 delete camera; 649 delete braid[0]; 650 delete braid[1]; 651 delete environment; 652 } 653 isYoungLara654 bool isYoung() { 655 return level->id == TR::LVL_TR4_ANGKOR1 || level->id == TR::LVL_TR4_ANG_RACE; 656 } 657 canSaveGameLara658 bool canSaveGame() { 659 return health > 0.0f && !burn 660 && state != STATE_USE_KEY 661 && state != STATE_USE_PUZZLE; // && (state == STATE_STOP || state == STATE_TREAD || state == STATE_SURF_TREAD); 662 } 663 getItemHandsLara664 TR::Entity::Type getItemHands() { 665 return (wpnState == Weapon::IS_HIDDEN) ? TR::Entity::NONE : wpnCurrent; 666 } 667 getItemBackLara668 TR::Entity::Type getItemBack() { 669 if (game->invCount(TR::Entity::INV_SHOTGUN) && (wpnCurrent != TR::Entity::SHOTGUN || wpnState == Weapon::IS_HIDDEN)) 670 return TR::Entity::SHOTGUN; 671 return TR::Entity::NONE; 672 } 673 getSaveDataLara674 virtual bool getSaveData(SaveEntity &data) { 675 if (camera->cameraIndex != 0) // only player1 can be saved 676 return false; 677 678 Character::getSaveData(data); 679 data.extraSize = sizeof(data.extra.lara); 680 data.extra.lara.velX = velocity.x; 681 data.extra.lara.velY = velocity.y; 682 data.extra.lara.velZ = velocity.z; 683 data.extra.lara.angleX = angle.x; 684 data.extra.lara.health = health; 685 data.extra.lara.oxygen = oxygen; 686 data.extra.lara.stamina = 0.0f; 687 data.extra.lara.poison = 0.0f; 688 data.extra.lara.freeze = 0.0f; 689 data.extra.lara.reserved = 0; 690 data.extra.lara.itemWeapon = wpnCurrent; 691 data.extra.lara.itemHands = getItemHands(); 692 data.extra.lara.itemBack = getItemBack(); 693 data.extra.lara.itemHolster = itemHolster; 694 data.extra.lara.spec.value = 0; 695 data.extra.lara.spec.burn = burn; 696 data.extra.lara.spec.wet = specular > LARA_MIN_SPECULAR; 697 return true; 698 } 699 setSaveDataLara700 virtual void setSaveData(const SaveEntity &data) { 701 Character::setSaveData(data); 702 statsDistDelta = 0.0f; 703 704 velocity = vec3(data.extra.lara.velX, data.extra.lara.velY, data.extra.lara.velZ); 705 angle.x = TR::angle(data.extra.lara.angleX); 706 health = data.extra.lara.health; 707 oxygen = data.extra.lara.oxygen; 708 709 if (level->isHome()) return; 710 711 layers[1].mask = layers[2].mask = layers[3].mask = 0; 712 713 if (data.extra.lara.itemWeapon) 714 wpnSet(TR::Entity::Type(data.extra.lara.itemWeapon)); 715 716 wpnCurrent = TR::Entity::Type(data.extra.lara.itemWeapon); 717 itemHolster = TR::Entity::Type(data.extra.lara.itemHolster); 718 wpnState = Weapon::IS_HIDDEN; 719 720 if (data.extra.lara.itemHands && data.extra.lara.itemHands == data.extra.lara.itemWeapon) 721 wpnDraw(true); 722 723 if (itemHolster != TR::Entity::NONE) 724 meshSwap(1, level->extra.weapons[itemHolster], JOINT_MASK_LEG_L1 | JOINT_MASK_LEG_R1); 725 726 if (getRoom().flags.water) { 727 stand = STAND_UNDERWATER; 728 if (state == STATE_SURF_TREAD || state == STATE_SURF_SWIM || state == STATE_SURF_BACK || state == STATE_SURF_LEFT || state == STATE_SURF_RIGHT) 729 stand = STAND_ONWATER; 730 } else 731 stand = STAND_AIR; 732 } 733 getRoomByPosLara734 int getRoomByPos(const vec3 &pos) { 735 int x = int(pos.x), 736 y = int(pos.y), 737 z = int(pos.z); 738 739 for (int i = 0; i < level->roomsCount; i++) { 740 TR::Room &r = level->rooms[i]; 741 int mx = r.info.x + r.xSectors * 1024; 742 int mz = r.info.z + r.zSectors * 1024; 743 if (x >= r.info.x && x < mx && z >= r.info.z && z < mz && y >= r.info.yTop && y < r.info.yBottom) 744 return i; 745 } 746 return TR::NO_ROOM; 747 } 748 749 void reset(int room, const vec3 &pos, float angle, Stand forceStand = STAND_GROUND) { 750 statsDistDelta = 0.0f; 751 752 visibleMask = 0xFFFFFFFF; 753 health = LARA_MAX_HEALTH; 754 oxygen = LARA_MAX_OXYGEN; 755 dozy = false; 756 757 keyHole = NULL; 758 keyItem = NULL; 759 760 if (room == TR::NO_ROOM) { 761 stand = STAND_AIR; 762 room = getRoomByPos(pos); 763 } 764 765 if (room == TR::NO_ROOM) 766 return; 767 768 if (level->rooms[room].flags.water) { 769 stand = STAND_UNDERWATER; 770 animation.setAnim(ANIM_UNDERWATER); 771 } else { 772 stand = STAND_GROUND; 773 animation.setAnim(ANIM_STAND); 774 } 775 776 velocity = vec3(0.0f); 777 778 roomIndex = room; 779 this->pos = pos; 780 this->angle = vec3(0.0f, angle, 0.0f); 781 782 if (forceStand != STAND_GROUND) { 783 stand = forceStand; 784 switch (stand) { 785 case STAND_ONWATER : animation.setAnim(ANIM_TO_ONWATER); break; 786 case STAND_UNDERWATER : animation.setAnim(ANIM_UNDERWATER); break; 787 default : ; 788 } 789 } 790 791 updateZone(); 792 updateLights(false); 793 794 camera->changeView(camera->firstPerson); 795 } 796 wpnSetLara797 void wpnSet(TR::Entity::Type wType) { 798 wpnCurrent = wType; 799 wpnState = Weapon::IS_FIRING; 800 801 arms[0].animation = arms[1].animation = Animation(level, &level->models[wType == TR::Entity::SHOTGUN ? TR::MODEL_SHOTGUN : TR::MODEL_PISTOLS]); 802 803 wpnSetAnim(arms[0], Weapon::IS_HIDDEN, Weapon::Anim::NONE, 0.0f, 0.0f); 804 wpnSetAnim(arms[1], Weapon::IS_HIDDEN, Weapon::Anim::NONE, 0.0f, 0.0f); 805 } 806 807 void wpnSetAnim(Arm &arm, Weapon::State wState, Weapon::Anim::Type wAnim, float wAnimTime, float wAnimDir, bool playing = true) { 808 arm.animation.setAnim(wpnGetAnimIndex(wAnim), 0, wAnim == Weapon::Anim::FIRE); 809 arm.animation.dir = playing ? wAnimDir : 0.0f; 810 811 if (arm.anim != wAnim) 812 arm.animation.frameIndex = 0xFFFF; 813 814 arm.anim = wAnim; 815 816 if (wAnimDir > 0.0f) 817 arm.animation.time = wAnimTime; 818 else 819 if (wAnimDir < 0.0f) 820 arm.animation.time = arm.animation.timeMax + wAnimTime; 821 arm.animation.updateInfo(); 822 823 wpnSetState(wState); 824 } 825 wpnGetDamageLara826 float wpnGetDamage() { 827 switch (wpnCurrent) { 828 case TR::Entity::PISTOLS : return 1; 829 case TR::Entity::SHOTGUN : return 3; 830 case TR::Entity::MAGNUMS : return 2; 831 case TR::Entity::UZIS : return 1; 832 default : ; 833 } 834 return 0; 835 } 836 wpnSetStateLara837 void wpnSetState(Weapon::State wState) { 838 if (wpnState == wState || !layers) return; 839 840 int mask = 0; 841 switch (wpnCurrent) { 842 case TR::Entity::PISTOLS : 843 case TR::Entity::MAGNUMS : 844 case TR::Entity::UZIS : 845 switch (wState) { 846 case Weapon::IS_HIDDEN : mask = JOINT_MASK_LEG_L1 | JOINT_MASK_LEG_R1; break; 847 case Weapon::IS_ARMED : mask = JOINT_MASK_ARM_L3 | JOINT_MASK_ARM_R3; break; 848 case Weapon::IS_FIRING : mask = JOINT_MASK_ARM_L3 | JOINT_MASK_ARM_R3 | JOINT_MASK_HEAD; break; 849 } 850 break; 851 case TR::Entity::SHOTGUN : 852 switch (wState) { 853 case Weapon::IS_HIDDEN : mask = JOINT_MASK_CHEST; break; 854 case Weapon::IS_ARMED : mask = JOINT_MASK_ARM_L3 | JOINT_MASK_ARM_R3; break; 855 case Weapon::IS_FIRING : mask = JOINT_MASK_ARM_L3 | JOINT_MASK_ARM_R3 | JOINT_MASK_HEAD; break; 856 } 857 break; 858 default : ; 859 } 860 861 if (wpnState == Weapon::IS_HIDDEN && wState == Weapon::IS_ARMED) game->playSound(TR::SND_UNHOLSTER, pos, Sound::PAN); 862 if (wpnState == Weapon::IS_ARMED && wState == Weapon::IS_HIDDEN) game->playSound(TR::SND_HOLSTER, pos, Sound::PAN); 863 864 // swap layers 865 // 0 - body (full) 866 // 1 - legs (hands, legs) 867 // 2 - shotgun (hands, chest) 868 // 3 - angry (head) 869 870 // swap weapon parts 871 if (wpnCurrent != TR::Entity::SHOTGUN) { 872 meshSwap(1, level->extra.weapons[wpnCurrent], mask); 873 // have a shotgun in inventory place it on the back if another weapon is in use 874 meshSwap(2, level->extra.weapons[TR::Entity::SHOTGUN], game->invCount(TR::Entity::INV_SHOTGUN) ? JOINT_MASK_CHEST : 0); 875 itemHolster = (wState == Weapon::IS_HIDDEN) ? wpnCurrent : TR::Entity::NONE; 876 } else { 877 meshSwap(2, level->extra.weapons[wpnCurrent], mask); 878 } 879 880 // mesh swap to angry Lara's head while firing (from uzis model) 881 meshSwap(3, level->extra.weapons[TR::Entity::UZIS], (wState == Weapon::IS_FIRING) ? JOINT_MASK_HEAD : 0); 882 883 wpnState = wState; 884 } 885 emptyHandsLara886 bool emptyHands() { 887 return wpnCurrent == TR::Entity::NONE || arms[0].anim == Weapon::Anim::NONE; 888 } 889 canLookAtLara890 bool canLookAt() { 891 return (stand != STAND_HANG) 892 && state != STATE_REACH 893 && state != STATE_PUSH_BLOCK 894 && state != STATE_PULL_BLOCK 895 && state != STATE_PUSH_PULL_READY 896 && state != STATE_PICK_UP; 897 } 898 canDrawWeaponLara899 bool canDrawWeapon() { 900 if (wpnCurrent == TR::Entity::NONE) 901 return false; 902 903 if (dozy) return true; 904 905 return animation.index != ANIM_CLIMB_3 906 && animation.index != ANIM_CLIMB_2 907 && state != STATE_DEATH 908 && state != STATE_HANG 909 && state != STATE_REACH 910 && state != STATE_TREAD 911 && state != STATE_SWIM 912 && state != STATE_GLIDE 913 && state != STATE_HANG_UP 914 && state != STATE_FALL_BACK 915 && state != STATE_HANG_LEFT 916 && state != STATE_HANG_RIGHT 917 && state != STATE_SURF_TREAD 918 && state != STATE_SURF_SWIM 919 && state != STATE_DIVE 920 && state != STATE_PUSH_BLOCK 921 && state != STATE_PULL_BLOCK 922 && state != STATE_PUSH_PULL_READY 923 && state != STATE_PICK_UP 924 && state != STATE_SWITCH_DOWN 925 && state != STATE_SWITCH_UP 926 && state != STATE_USE_KEY 927 && state != STATE_USE_PUZZLE 928 && state != STATE_UNDERWATER_DEATH 929 && state != STATE_SPECIAL 930 && state != STATE_SURF_BACK 931 && state != STATE_SURF_LEFT 932 && state != STATE_SURF_RIGHT 933 && state != STATE_HANDSTAND 934 && state != STATE_WATER_OUT; 935 } 936 canHitAnimLara937 bool canHitAnim() { 938 return state == STATE_WALK 939 || state == STATE_RUN 940 || state == STATE_STOP 941 || state == STATE_FAST_BACK 942 || state == STATE_TURN_RIGHT 943 || state == STATE_TURN_LEFT 944 || state == STATE_BACK 945 || state == STATE_FAST_TURN 946 || state == STATE_STEP_RIGHT 947 || state == STATE_STEP_LEFT; 948 } 949 wpnReadyLara950 bool wpnReady() { 951 return arms[0].anim != Weapon::Anim::PREPARE && arms[0].anim != Weapon::Anim::UNHOLSTER && arms[0].anim != Weapon::Anim::HOLSTER; 952 } 953 954 void wpnDraw(bool instant = false) { 955 if (!canDrawWeapon()) return; 956 957 if (wpnReady() && emptyHands()) { 958 if (wpnCurrent != TR::Entity::SHOTGUN) { 959 wpnSetAnim(arms[0], wpnState, instant ? Weapon::Anim::AIM : Weapon::Anim::PREPARE, 0.0f, 1.0f); 960 wpnSetAnim(arms[1], wpnState, instant ? Weapon::Anim::AIM : Weapon::Anim::PREPARE, 0.0f, 1.0f); 961 } else 962 wpnSetAnim(arms[0], wpnState, instant ? Weapon::Anim::AIM : Weapon::Anim::UNHOLSTER, 0.0f, 1.0f); 963 } 964 } 965 wpnHideLara966 void wpnHide() { 967 if (wpnReady() && !emptyHands()) { 968 if (wpnCurrent != TR::Entity::SHOTGUN) { 969 wpnSetAnim(arms[0], wpnState, Weapon::Anim::UNHOLSTER, 0.0f, -1.0f); 970 wpnSetAnim(arms[1], wpnState, Weapon::Anim::UNHOLSTER, 0.0f, -1.0f); 971 } else 972 wpnSetAnim(arms[0], wpnState, Weapon::Anim::HOLSTER, 0.0f, 1.0f); 973 } 974 } 975 wpnChangeLara976 void wpnChange(TR::Entity::Type wType) { 977 if (wpnCurrent == wType || level->isHome()) { 978 if (emptyHands()) 979 wpnDraw(); 980 return; 981 } 982 wpnNext = wType; 983 wpnHide(); 984 } 985 wpnGetAnimIndexLara986 int wpnGetAnimIndex(Weapon::Anim::Type wAnim) { 987 if (wpnCurrent == TR::Entity::SHOTGUN) { 988 switch (wAnim) { 989 case Weapon::Anim::PREPARE : ASSERT(false); break; // rifle has no prepare animation 990 case Weapon::Anim::UNHOLSTER : return 1; 991 case Weapon::Anim::HOLSTER : return 3; 992 case Weapon::Anim::HOLD : 993 case Weapon::Anim::AIM : return 0; 994 case Weapon::Anim::FIRE : return 2; 995 default : ; 996 } 997 } else 998 switch (wAnim) { 999 case Weapon::Anim::PREPARE : return 1; 1000 case Weapon::Anim::UNHOLSTER : return 2; 1001 case Weapon::Anim::HOLSTER : ASSERT(false); break; // pistols has no holster animation (it's reversed unholster) 1002 case Weapon::Anim::HOLD : 1003 case Weapon::Anim::AIM : return 0; 1004 case Weapon::Anim::FIRE : return 3; 1005 default : ; 1006 } 1007 return 0; 1008 } 1009 wpnGetSoundLara1010 int wpnGetSound() { 1011 switch (wpnCurrent) { 1012 case TR::Entity::PISTOLS : return TR::SND_PISTOLS_SHOT; 1013 case TR::Entity::SHOTGUN : return TR::SND_SHOTGUN_SHOT; 1014 case TR::Entity::MAGNUMS : return TR::SND_MAGNUMS_SHOT; 1015 case TR::Entity::UZIS : return TR::SND_UZIS_SHOT; 1016 default : return TR::SND_NO; 1017 } 1018 } 1019 wpnFireLara1020 void wpnFire() { 1021 bool armShot[2] = { false, false }; 1022 for (int i = 0; i < 2; i++) { 1023 Arm &arm = arms[i]; 1024 if (arm.anim == Weapon::Anim::FIRE) { 1025 Animation &anim = arm.animation; 1026 //int realFrameIndex = int(arms[i].animation.time * 30.0f / anim->frameRate) % ((anim->frameEnd - anim->frameStart) / anim->frameRate + 1); 1027 if (anim.frameIndex != anim.framePrev) { 1028 if (anim.frameIndex == 0) { //realFrameIndex < arms[i].animation.framePrev) { 1029 if ((input & ACTION) && (!arm.tracking || arm.target)) 1030 armShot[i] = true; 1031 else 1032 wpnSetAnim(arm, Weapon::IS_ARMED, Weapon::Anim::AIM, 0.0f, -1.0f, arm.target == NULL); 1033 } 1034 // shotgun reload sound 1035 if (wpnCurrent == TR::Entity::SHOTGUN) { 1036 if (anim.frameIndex == 10) 1037 game->playSound(TR::SND_SHOTGUN_RELOAD, pos, Sound::PAN); 1038 } 1039 } 1040 } 1041 arm.animation.framePrev = arm.animation.frameIndex; 1042 1043 if (wpnCurrent == TR::Entity::SHOTGUN) break; 1044 } 1045 1046 if (armShot[0] || armShot[1]) 1047 doShot(armShot[0], armShot[1]); 1048 } 1049 doShotLara1050 void doShot(bool rightHand, bool leftHand) { 1051 int *wpnAmmo = game->invCount(wpnCurrent); 1052 1053 if (wpnAmmo && *wpnAmmo != UNLIMITED_AMMO && *wpnAmmo <= 0) { // check for no ammo 1054 game->playSound(TR::SND_EMPTY, pos, Sound::PAN); 1055 wpnChange(TR::Entity::PISTOLS); 1056 } 1057 1058 int count = wpnCurrent == TR::Entity::SHOTGUN ? 6 : 2; 1059 float nearDist = 32.0f * 1024.0f; 1060 vec3 nearPos; 1061 int shots = 0, hits = 0; 1062 1063 for (int i = 0; i < count; i++) { 1064 int armIndex; 1065 if (wpnCurrent == TR::Entity::SHOTGUN) { 1066 if (!rightHand) continue; 1067 armIndex = 0; 1068 } else { 1069 if (!(i ? leftHand : rightHand)) continue; 1070 armIndex = i; 1071 } 1072 Arm *arm = &arms[armIndex]; 1073 1074 if (wpnAmmo && *wpnAmmo != UNLIMITED_AMMO) { 1075 if (*wpnAmmo <= 0) 1076 continue; 1077 if (wpnCurrent != TR::Entity::SHOTGUN) 1078 *wpnAmmo -= 1; 1079 } 1080 1081 shots++; 1082 1083 if (wpnCurrent != TR::Entity::SHOTGUN) 1084 game->addMuzzleFlash(this, i ? LARA_LGUN_JOINT : LARA_RGUN_JOINT, i ? LARA_LGUN_OFFSET : LARA_RGUN_OFFSET, 1 + camera->cameraIndex); 1085 1086 // TODO: use new trace code 1087 int joint = wpnCurrent == TR::Entity::SHOTGUN ? 8 : (i ? 11 : 8); 1088 vec3 p = getJoint(joint).pos; 1089 vec3 d = arm->rotAbs * vec3(0, 0, 1); 1090 vec3 t = p + d * (24.0f * 1024.0f) + ((vec3(randf(), randf(), randf()) * 2.0f) - vec3(1.0f)) * 1024.0f; 1091 1092 int room; 1093 vec3 hit = trace(getRoomIndex(), p, t, room, false); 1094 if (arm->target && checkHit(arm->target, p, hit, hit)) { 1095 hits++; 1096 TR::Entity::Type type = arm->target->getEntity().type; 1097 ((Character*)arm->target)->hit(wpnGetDamage(), this); 1098 hit -= d * 64.0f; 1099 if (type != TR::Entity::SCION_TARGET) 1100 game->addEntity(TR::Entity::BLOOD, room, hit); 1101 } else { 1102 hit -= d * 64.0f; 1103 game->addEntity(TR::Entity::RICOCHET, room, hit); 1104 1105 float dist = (hit - p).length(); 1106 if (dist < nearDist) { 1107 nearPos = hit; 1108 nearDist = dist; 1109 } 1110 } 1111 } 1112 1113 if (shots) { 1114 saveStats.ammoUsed += ((wpnCurrent == TR::Entity::SHOTGUN) ? 1 : 2); 1115 1116 game->playSound(wpnGetSound(), pos, Sound::PAN); 1117 if (shots != hits) 1118 game->playSound(TR::SND_RICOCHET, nearPos, Sound::PAN); 1119 1120 if (wpnAmmo && *wpnAmmo != UNLIMITED_AMMO && wpnCurrent == TR::Entity::SHOTGUN) 1121 *wpnAmmo -= 1; 1122 } 1123 } 1124 updateWeaponLara1125 void updateWeapon() { 1126 if (level->isCutsceneLevel()) return; 1127 1128 if (wpnNext != TR::Entity::NONE && emptyHands()) { 1129 wpnSet(wpnNext); 1130 wpnDraw(); 1131 wpnNext = TR::Entity::NONE; 1132 } 1133 1134 // apply weapon state changes 1135 if (input & WEAPON) { 1136 if (emptyHands()) 1137 wpnDraw(); 1138 else 1139 wpnHide(); 1140 } 1141 1142 if (!emptyHands()) { 1143 bool isRifle = wpnCurrent == TR::Entity::SHOTGUN; 1144 1145 for (int i = 0; i < 2; i++) { 1146 Arm &arm = arms[i]; 1147 1148 if (arm.target || ((input & ACTION) && !arm.tracking)) { 1149 if (arm.anim == Weapon::Anim::HOLD) 1150 wpnSetAnim(arm, wpnState, Weapon::Anim::AIM, 0.0f, 1.0f); 1151 } else 1152 if (arm.anim == Weapon::Anim::AIM) 1153 arm.animation.dir = -1.0f; 1154 1155 if (isRifle) break; 1156 } 1157 1158 for (int i = 0; i < 2; i++) 1159 arms[i].animation.update(); 1160 1161 if (isRifle) 1162 animateShotgun(); 1163 else 1164 animatePistols(); 1165 1166 wpnFire(); // make a shot 1167 } 1168 } 1169 animatePistolsLara1170 void animatePistols() { 1171 for (int i = 0; i < 2; i++) { 1172 Arm &arm = arms[i]; 1173 1174 if (!arm.animation.isEnded) continue; 1175 1176 if (arm.animation.dir >= 0.0f) 1177 switch (arm.anim) { 1178 case Weapon::Anim::PREPARE : wpnSetAnim(arm, Weapon::IS_ARMED, Weapon::Anim::UNHOLSTER, arm.animation.time - arm.animation.timeMax, 1.0f); break; 1179 case Weapon::Anim::UNHOLSTER : wpnSetAnim(arm, Weapon::IS_ARMED, Weapon::Anim::HOLD, 0.0f, 1.0f, false); break; 1180 case Weapon::Anim::AIM : 1181 case Weapon::Anim::FIRE : 1182 if (input & ACTION) 1183 wpnSetAnim(arm, Weapon::IS_FIRING, Weapon::Anim::FIRE, arm.animation.time - arm.animation.timeMax, wpnCurrent == TR::Entity::UZIS ? 2.0f : 1.0f); 1184 else 1185 wpnSetAnim(arm, Weapon::IS_ARMED, Weapon::Anim::AIM, 0.0f, -1.0f, false); 1186 break; 1187 default : ; 1188 }; 1189 1190 if (arm.animation.dir < 0.0f) 1191 switch (arm.anim) { 1192 case Weapon::Anim::PREPARE : wpnSetAnim(arm, Weapon::IS_HIDDEN, Weapon::Anim::NONE, 0.0f, 1.0f, false); break; 1193 case Weapon::Anim::UNHOLSTER : wpnSetAnim(arm, Weapon::IS_HIDDEN, Weapon::Anim::PREPARE, arm.animation.time, -1.0f); break; 1194 case Weapon::Anim::AIM : wpnSetAnim(arm, Weapon::IS_ARMED, Weapon::Anim::HOLD, 0.0f, 1.0f, false); break; 1195 default : ; 1196 }; 1197 } 1198 } 1199 animateShotgunLara1200 void animateShotgun() { 1201 Arm &arm = arms[0]; 1202 if (arm.animation.dir >= 0.0f) { 1203 if (arm.animation.isEnded) { 1204 switch (arm.anim) { 1205 case Weapon::Anim::UNHOLSTER : wpnSetAnim(arm, Weapon::IS_ARMED, Weapon::Anim::HOLD, 0.0f, 1.0f, false); break; 1206 case Weapon::Anim::HOLSTER : wpnSetAnim(arm, Weapon::IS_HIDDEN, Weapon::Anim::NONE, 0.0f, 1.0f, false); break; 1207 case Weapon::Anim::AIM : 1208 case Weapon::Anim::FIRE : 1209 if (input & ACTION) 1210 wpnSetAnim(arm, Weapon::IS_FIRING, Weapon::Anim::FIRE, arm.animation.time - arm.animation.timeMax, 1.0f); 1211 else 1212 wpnSetAnim(arm, Weapon::IS_ARMED, Weapon::Anim::AIM, 0.0f, -1.0f, false); 1213 break; 1214 default : ; 1215 } 1216 } else 1217 if (arm.animation.frameIndex != arm.animation.framePrev) { 1218 float delta = arm.animation.time / arm.animation.timeMax; 1219 switch (arm.anim) { 1220 case Weapon::Anim::UNHOLSTER : if (delta >= 0.3f) wpnSetAnim(arm, Weapon::IS_ARMED, arm.anim, arm.animation.time, 1.0f); break; 1221 case Weapon::Anim::HOLSTER : if (delta >= 0.7f) wpnSetAnim(arm, Weapon::IS_HIDDEN, arm.anim, arm.animation.time, 1.0f); break; 1222 default : ; 1223 } 1224 } 1225 } else 1226 if (arm.animation.isEnded && arm.anim == Weapon::Anim::AIM) 1227 wpnSetAnim(arm, Weapon::IS_ARMED, Weapon::Anim::HOLD, 0.0f, 1.0f, false); 1228 } 1229 updateOverridesLara1230 void updateOverrides() { 1231 // Copy all current animation joints 1232 for (int i = 0; i < JOINT_MAX; i++) 1233 animation.overrides[i] = animation.getJointRot(i); 1234 1235 int overrideMask = 0; 1236 // head & chest 1237 overrideMask |= JOINT_MASK_CHEST | JOINT_MASK_HEAD; 1238 1239 // update hit anim 1240 if (hitDir >= 0) { 1241 Animation hitAnim = Animation(level, getModel()); 1242 switch (hitDir) { 1243 case 0 : hitAnim.setAnim(ANIM_HIT_FRONT, 0, false); break; 1244 case 1 : hitAnim.setAnim(ANIM_HIT_LEFT, 0, false); break; 1245 case 2 : hitAnim.setAnim(ANIM_HIT_BACK , 0, false); break; 1246 case 3 : hitAnim.setAnim(ANIM_HIT_RIGHT, 0, false); break; 1247 } 1248 hitTime = min(hitTime, hitAnim.timeMax - EPS); 1249 hitAnim.time = hitTime; 1250 hitAnim.updateInfo(); 1251 1252 overrideMask &= ~(JOINT_MASK_CHEST | JOINT_MASK_HEAD); 1253 int hitMask = (JOINT_MASK_UPPER | JOINT_MASK_LOWER | JOINT_MASK_HEAD) & ~overrideMask; 1254 int index = 0; 1255 while (hitMask) { 1256 if (hitMask & 1) 1257 animation.overrides[index] = hitAnim.getJointRot(index); 1258 index++; 1259 hitMask >>= 1; 1260 } 1261 1262 hitTime += Core::deltaTime; 1263 overrideMask = JOINT_MASK_UPPER | JOINT_MASK_LOWER | JOINT_MASK_HEAD; 1264 } 1265 1266 // arms 1267 if (!emptyHands()) { 1268 // right arm 1269 Arm *arm = &arms[0]; 1270 animation.overrides[JOINT_ARM_R1] = animation.overrides[JOINT_CHEST].inverse() * animation.overrides[JOINT_HIPS].inverse() * arm->animation.getJointRot(JOINT_ARM_R1); 1271 animation.overrides[JOINT_ARM_R2] = arm->animation.getJointRot(JOINT_ARM_R2); 1272 animation.overrides[JOINT_ARM_R3] = arm->animation.getJointRot(JOINT_ARM_R3); 1273 // left arm 1274 if (wpnCurrent != TR::Entity::SHOTGUN) arm = &arms[1]; 1275 1276 animation.overrides[JOINT_ARM_L1] = animation.overrides[JOINT_CHEST].inverse() * animation.overrides[JOINT_HIPS].inverse() * arm->animation.getJointRot(JOINT_ARM_L1); 1277 animation.overrides[JOINT_ARM_L2] = arm->animation.getJointRot(JOINT_ARM_L2); 1278 animation.overrides[JOINT_ARM_L3] = arm->animation.getJointRot(JOINT_ARM_L3); 1279 1280 overrideMask |= (JOINT_MASK_ARM_R | JOINT_MASK_ARM_L); 1281 } else 1282 overrideMask &= ~(JOINT_MASK_ARM_R | JOINT_MASK_ARM_L); 1283 1284 animation.overrideMask = overrideMask; 1285 jointsFrame = -1; 1286 } 1287 getAngleLara1288 vec3 getAngle(const vec3 &dir) { 1289 return vec3(atan2f(dir.y, sqrtf(dir.x * dir.x + dir.z * dir.z)) - angle.x, atan2f(dir.x, dir.z) - angle.y + PI, 0.0f); 1290 } 1291 getAngleAbsLara1292 vec3 getAngleAbs(const vec3 &dir) { 1293 return vec3(-atan2f(dir.y, sqrtf(dir.x * dir.x + dir.z * dir.z)), -atan2f(dir.x, dir.z), 0.0f); 1294 } 1295 lookAtLara1296 virtual void lookAt(Controller *target) { 1297 if (health <= 0.0f) 1298 return; 1299 1300 updateOverrides(); 1301 updateTargets(); 1302 1303 Controller *lookTarget = canLookAt() ? target : NULL; 1304 if (camera->mode == Camera::MODE_LOOK) { 1305 vec3 p = pos + vec3(camera->targetAngle.x, camera->targetAngle.y) * 8192.0f; 1306 Character::lookAtPos(&p); 1307 } else 1308 Character::lookAt(lookTarget); 1309 1310 if (wpnReady() && !emptyHands()) { 1311 if (wpnCurrent == TR::Entity::SHOTGUN) 1312 aimShotgun(); 1313 else 1314 aimPistols(); 1315 } 1316 1317 if (target) { 1318 Box box = target->getBoundingBox(); 1319 vec3 dir = getJoint(jointHead).pos - box.center(); 1320 vec3 angle = getAngle(dir) * RAD2DEG; 1321 camera->setAngle(angle.x, angle.y); 1322 } else { 1323 if (camera->mode == ICamera::MODE_COMBAT) { 1324 camera->setAngle(0, 0); 1325 } 1326 } 1327 } 1328 aimShotgunLara1329 void aimShotgun() { 1330 float speed = 8.0f * Core::deltaTime; 1331 1332 int joints[2] = { JOINT_ARM_R1, JOINT_ARM_L1 }; 1333 1334 bool hasAim = true; 1335 1336 quat rot; 1337 Arm &arm = arms[0]; 1338 if (!aim(arm.target, JOINT_ARM_R1, vec4(-PI * 0.4f, PI * 0.4f, -PI * 0.25f, PI * 0.25f), rot, &arm.rotAbs)) { 1339 rot = quat(0, 0, 0, 1); 1340 arm.target = NULL; 1341 hasAim = false; 1342 } 1343 1344 if (hasAim) 1345 rot = animation.getJointRot(JOINT_HIPS) * animation.getJointRot(JOINT_CHEST) * rot; 1346 1347 for (int i = 0; i < 2; i++) { 1348 int j = joints[i]; 1349 arm.rot = arm.rot.slerp(rot, speed); 1350 animation.overrides[j] = arm.rot * animation.overrides[j]; 1351 jointsFrame = -1; 1352 } 1353 } 1354 aimPistolsLara1355 void aimPistols() { 1356 float speed = 8.0f * Core::deltaTime; 1357 1358 int joints[2] = { JOINT_ARM_R1, JOINT_ARM_L1 }; 1359 1360 vec4 ranges[2] = { 1361 vec4(-PI * 0.4f, PI * 0.4f, -PI * 0.2f, PI * 0.5f), 1362 vec4(-PI * 0.4f, PI * 0.4f, -PI * 0.5f, PI * 0.2f), 1363 }; 1364 1365 for (int i = 0; i < 2; i++) { 1366 quat rot; 1367 Arm &arm = arms[i]; 1368 int j = joints[i]; 1369 1370 bool hasAim = true; 1371 if (!aim(arm.target, j, ranges[i], rot, &arm.rotAbs)) { 1372 arm.target = arms[i^1].target; 1373 if (!aim(arm.target, j, ranges[i], rot, &arm.rotAbs)) { 1374 rot = quat(0, 0, 0, 1); 1375 arm.target = NULL; 1376 hasAim = false; 1377 } 1378 } 1379 1380 if (hasAim) 1381 rot = animation.getJointRot(JOINT_HIPS) * animation.getJointRot(JOINT_CHEST) * rot; 1382 1383 arm.rot = arm.rot.slerp(rot, speed); 1384 animation.overrides[j] = arm.rot * animation.overrides[j]; 1385 jointsFrame = -1; 1386 } 1387 } 1388 updateTargetsLara1389 void updateTargets() { 1390 arms[0].target = arms[1].target = NULL; 1391 viewTarget = NULL; 1392 1393 if (emptyHands() || !wpnReady()) { 1394 arms[0].tracking = arms[1].tracking = NULL; 1395 return; 1396 } 1397 1398 // auto retarget 1399 bool retarget = false; 1400 if (Core::settings.controls[camera->cameraIndex].retarget) { 1401 for (int i = 0; i < 2; i++) 1402 if (!arms[i].tracking || !((Character*)arms[i].tracking)->isActiveTarget()) { 1403 retarget = true; 1404 break; 1405 } 1406 } 1407 1408 int count = wpnCurrent != TR::Entity::SHOTGUN ? 2 : 1; 1409 if (!(input & ACTION) || retarget) { 1410 getTargets(arms[0].tracking, arms[1].tracking); 1411 if (count == 1) 1412 arms[1].tracking = NULL; 1413 else if (!arms[0].tracking && arms[1].tracking) 1414 arms[0].tracking = arms[1].tracking; 1415 else if (!arms[1].tracking && arms[0].tracking) 1416 arms[1].tracking = arms[0].tracking; 1417 arms[0].target = arms[0].tracking; 1418 arms[1].target = arms[1].tracking; 1419 } else { 1420 if (!arms[0].tracking && !arms[1].tracking) 1421 return; 1422 1423 // flip left and right by relative target direction 1424 if (count > 1) { 1425 float side[2] = { 0, 0 }; 1426 vec3 dir = getDir(); 1427 dir.y = 0.0f; 1428 1429 for (int i = 0; i < count; i++) 1430 if (arms[i].tracking) { 1431 vec3 v = arms[i].tracking->pos - pos; 1432 v.y = 0; 1433 side[i] = sign(v.cross(dir).y); 1434 } 1435 1436 if (side[0] > 0 && side[1] < 0) 1437 swap(arms[0].tracking, arms[1].tracking); 1438 } 1439 1440 // check occlusion for tracking targets 1441 for (int i = 0; i < count; i++) 1442 if (arms[i].tracking) { 1443 Controller *enemy = (Controller*)arms[i].tracking; 1444 1445 Box box = enemy->getBoundingBox(); 1446 vec3 to = box.center(); 1447 to.y = box.min.y + (box.max.y - box.min.y) / 3.0f; 1448 1449 vec3 from = pos - vec3(0, 650, 0); 1450 arms[i].target = checkOcclusion(from, to, (to - from).length()) ? arms[i].tracking : NULL; 1451 } 1452 1453 if (count == 1) 1454 arms[1].target = NULL; 1455 else if (!arms[0].target && arms[1].target) 1456 arms[0].target = arms[1].target; 1457 else if (!arms[1].target && arms[0].target) 1458 arms[1].target = arms[0].target; 1459 } 1460 1461 if (arms[0].target && arms[1].target && arms[0].target != arms[1].target) { 1462 viewTarget = (arms[0].target->pos - pos).length2() < (arms[1].target->pos - pos).length2() ? arms[0].target : arms[1].target; 1463 } else if (arms[0].target) 1464 viewTarget = arms[0].target; 1465 else if (arms[1].target) 1466 viewTarget = arms[1].target; 1467 else if (arms[0].tracking) 1468 viewTarget = arms[0].tracking; 1469 else if (arms[1].tracking) 1470 viewTarget = arms[1].tracking; 1471 } 1472 getTargetsLara1473 void getTargets(Controller *&target1, Controller *&target2) { 1474 vec3 dir = getDir().normal(); 1475 float dist[2] = { TARGET_MAX_DIST, TARGET_MAX_DIST }; 1476 1477 target1 = target2 = NULL; 1478 1479 vec3 from = pos - vec3(0, 650, 0); 1480 1481 Controller *c = Controller::first; 1482 do { 1483 if (!c->getEntity().isEnemy()) 1484 continue; 1485 1486 Character *enemy = (Character*)c; 1487 if (!enemy->isActiveTarget()) 1488 continue; 1489 1490 Box box = enemy->getBoundingBox(); 1491 vec3 p = box.center(); 1492 p.y = box.min.y + (box.max.y - box.min.y) / 3.0f; 1493 1494 vec3 v = p - pos; 1495 if (dir.dot(v.normal()) <= 0.5f) 1496 continue; // target is out of view range -60..+60 degrees 1497 1498 float d = v.length(); 1499 1500 if ((d > dist[0] && d > dist[1]) || !checkOcclusion(from, p, d)) 1501 continue; 1502 1503 if (d < dist[0]) { 1504 target2 = target1; 1505 dist[1] = dist[0]; 1506 target1 = enemy; 1507 dist[0] = d; 1508 } else if (d < dist[1]) { 1509 target2 = enemy; 1510 dist[1] = d; 1511 } 1512 } while ((c = c->next)); 1513 1514 if (!target2 || dist[1] > dist[0] * 4) 1515 target2 = target1; 1516 } 1517 checkOcclusionLara1518 bool checkOcclusion(const vec3 &from, const vec3 &to, float dist) { 1519 int room; 1520 vec3 d = trace(getRoomIndex(), from, to, room, false); // check occlusion 1521 return ((d - from).length() > (dist - 512.0f)); 1522 } 1523 checkHitLara1524 bool checkHit(Controller *target, const vec3 &from, const vec3 &to, vec3 &point) { 1525 Box box = target->getBoundingBoxLocal(); 1526 mat4 m = target->getMatrix(); 1527 1528 float t; 1529 vec3 v = to - from; 1530 1531 if (box.intersect(m, from, v, t)) { 1532 t *= v.length(); 1533 v = v.normal(); 1534 Sphere spheres[MAX_JOINTS]; 1535 int count = target->getSpheres(spheres); 1536 for (int i = 0; i < count; i++) { 1537 float st; 1538 if (spheres[i].intersect(from, v, st)) { 1539 point = from + v * max(t, st); 1540 return true; 1541 } 1542 } 1543 } 1544 return false; 1545 } 1546 cmdEmptyLara1547 virtual void cmdEmpty() { 1548 wpnHide(); 1549 } 1550 cmdOffsetLara1551 virtual void cmdOffset(const vec3 &offset) { 1552 Character::cmdOffset(offset); 1553 move(); 1554 } 1555 cmdJumpLara1556 virtual void cmdJump(const vec3 &vel) { 1557 vec3 v = vel; 1558 if (state == STATE_HANG_UP) 1559 v.y = (3.0f - sqrtf(-2.0f * GRAVITY * (collision.info[Collision::FRONT].floor - pos.y + 800.0f - 128.0f))); 1560 Character::cmdJump(v); 1561 } 1562 drawGunLara1563 void drawGun(int right) { 1564 wpnCurrent = TR::Entity::PISTOLS; 1565 int mask = (right ? JOINT_MASK_ARM_R3 : JOINT_MASK_ARM_L3); // unholster 1566 if (layers[1].mask & mask) 1567 mask = (layers[1].mask & ~mask) | (right ? JOINT_MASK_LEG_R1 : JOINT_MASK_LEG_L1); // holster 1568 else 1569 mask |= layers[1].mask; 1570 meshSwap(1, level->extra.weapons[wpnCurrent], mask); 1571 } 1572 doBubblesLara1573 void doBubbles() { 1574 if ((level->version & TR::VER_VERSION) > TR::VER_TR2) { 1575 return; // TODO 1576 } 1577 int count = rand() % 3; 1578 if (!count) return; 1579 game->playSound(TR::SND_BUBBLE, pos, Sound::PAN); 1580 vec3 head = getJoint(jointHead) * vec3(0.0f, 0.0f, 50.0f); 1581 for (int i = 0; i < count; i++) 1582 game->addEntity(TR::Entity::BUBBLE, getRoomIndex(), head, 0); 1583 } 1584 cmdEffectLara1585 virtual void cmdEffect(int fx) { 1586 switch (fx) { 1587 case TR::Effect::LARA_NORMAL : animation.setAnim(ANIM_STAND); break; 1588 case TR::Effect::LARA_BUBBLES : doBubbles(); break; 1589 case TR::Effect::LARA_HANDSFREE : break;//meshSwap(1, level->extra.weapons[wpnCurrent], BODY_LEG_L1 | BODY_LEG_R1); break; 1590 case TR::Effect::DRAW_RIGHTGUN : drawGun(true); break; 1591 case TR::Effect::DRAW_LEFTGUN : drawGun(false); break; 1592 case TR::Effect::SHOT_RIGHTGUN : game->addMuzzleFlash(this, LARA_RGUN_JOINT, LARA_RGUN_OFFSET, 1 + camera->cameraIndex); break; 1593 case TR::Effect::SHOT_LEFTGUN : game->addMuzzleFlash(this, LARA_LGUN_JOINT, LARA_LGUN_OFFSET, 1 + camera->cameraIndex); break; 1594 case TR::Effect::MESH_SWAP_1 : 1595 case TR::Effect::MESH_SWAP_2 : 1596 case TR::Effect::MESH_SWAP_3 : Character::cmdEffect(fx); 1597 case 26 : break; // TODO TR2 reset_hair 1598 case 32 : break; // TODO TR3 footprint 1599 default : LOG("unknown effect command %d (anim %d)\n", fx, animation.index); ASSERT(false); 1600 } 1601 } 1602 1603 virtual void hit(float damage, Controller *enemy = NULL, TR::HitType hitType = TR::HIT_DEFAULT) { 1604 if (dozy || level->isCutsceneLevel()) return; 1605 1606 if (health <= 0.0f && hitType != TR::HIT_FALL) return; 1607 1608 damageTime = LARA_DAMAGE_TIME; 1609 1610 Character::hit(damage, enemy, hitType); 1611 1612 hitTimer = LARA_VIBRATE_HIT_TIME; 1613 1614 switch (hitType) { 1615 case TR::HIT_DART : addBlood(enemy->pos, vec3(0)); 1616 case TR::HIT_BLADE : addBloodBlade(); break; 1617 case TR::HIT_SPIKES : addBloodSpikes(); break; 1618 case TR::HIT_SWORD : addBloodBlade(); break; 1619 case TR::HIT_SLAM : addBloodSlam(enemy); break; 1620 case TR::HIT_LIGHTNING : lightning = (Lightning*)enemy; break; 1621 default : ; 1622 } 1623 1624 if (health > 0.0f) 1625 return; 1626 1627 game->stopTrack(); 1628 1629 Core::lightColor[1 + 0] = Core::lightColor[1 + 1] = vec4(0, 0, 0, 1); 1630 arms[0].tracking = arms[1].tracking = NULL; 1631 arms[0].target = arms[1].target = NULL; 1632 viewTarget = NULL; 1633 animation.overrideMask = 0; 1634 int oldState = state; 1635 1636 switch (hitType) { 1637 case TR::HIT_FALL : { 1638 animation.setState(STATE_DEATH); 1639 break; 1640 } 1641 case TR::HIT_BOULDER : { 1642 animation.setAnim(ANIM_DEATH_BOULDER); 1643 1644 vec3 v(0.0f); 1645 if (enemy && enemy->getEntity().type == TR::Entity::TRAP_BOULDER) { 1646 angle = enemy->angle; 1647 TR::Level::FloorInfo info; 1648 getFloorInfo(getRoomIndex(), pos, info); 1649 vec3 d = getDir(); 1650 v = info.getSlant(d); 1651 float dp = d.dot(v); 1652 if (fabsf(dp) < 0.999) 1653 angle.x = -acosf(dp); 1654 v = ((TrapBoulder*)enemy)->velocity * 2.0f; 1655 } 1656 1657 for (int i = 0; i < 15; i++) 1658 addBlood(256.0f, 512.0f, v); 1659 break; 1660 } 1661 case TR::HIT_SPIKES : { 1662 pos.y = enemy->pos.y; 1663 animation.setAnim(ANIM_DEATH_SPIKES); 1664 for (int i = 0; i < 19; i++) 1665 addBloodSpikes(); 1666 break; 1667 } 1668 case TR::HIT_REX : { 1669 pos = enemy->pos; 1670 angle = enemy->angle; 1671 1672 meshSwap(1, TR::MODEL_LARA_SPEC, JOINT_MASK_UPPER | JOINT_MASK_LOWER); 1673 meshSwap(2, level->extra.weapons[TR::Entity::SHOTGUN], 0); 1674 meshSwap(3, level->extra.weapons[TR::Entity::UZIS], 0); 1675 1676 animation.setAnim(level->models[TR::MODEL_LARA_SPEC].animation + 1); 1677 break; 1678 } 1679 case TR::HIT_MIDAS : { 1680 // generate environment map for reflections 1681 bakeEnvironment(environment); 1682 // set death animation 1683 animation.setAnim(level->models[TR::MODEL_LARA_SPEC].animation + 1); 1684 camera->doCutscene(pos, angle.y); 1685 break; 1686 } 1687 case TR::HIT_GIANT_MUTANT : { 1688 pos = enemy->pos; 1689 angle = enemy->angle; 1690 1691 animation.setAnim(level->models[TR::MODEL_LARA_SPEC].animation); 1692 break; 1693 } 1694 1695 default : ; 1696 } 1697 1698 if (hitType != TR::HIT_LAVA) { 1699 TR::Level::FloorInfo info; 1700 getFloorInfo(getRoomIndex(), pos, info); 1701 1702 if (info.lava && info.floor == pos.y) 1703 hitType = TR::HIT_LAVA; 1704 } 1705 1706 if (hitType == TR::HIT_LAVA) { 1707 Flame::add(game, this, 0); 1708 for (int i = 1; i < 10; i++) 1709 Flame::add(game, this, rand() % getModel()->mCount); 1710 } 1711 1712 if (state != oldState) 1713 velocity = vec3(0.0f); 1714 }; 1715 useItemLara1716 bool useItem(TR::Entity::Type item) { 1717 if (game->isCutscene()) return false; 1718 1719 switch (item) { 1720 case TR::Entity::INV_PISTOLS : wpnChange(TR::Entity::PISTOLS); break; 1721 case TR::Entity::INV_SHOTGUN : wpnChange(TR::Entity::SHOTGUN); break; 1722 case TR::Entity::INV_MAGNUMS : wpnChange(TR::Entity::MAGNUMS); break; 1723 case TR::Entity::INV_UZIS : wpnChange(TR::Entity::UZIS); break; 1724 case TR::Entity::INV_MEDIKIT_SMALL : 1725 case TR::Entity::INV_MEDIKIT_BIG : 1726 saveStats.mediUsed += (item == TR::Entity::INV_MEDIKIT_SMALL) ? 1 : 2; 1727 usedItem = item; 1728 break; 1729 case TR::Entity::INV_PUZZLE_1 : 1730 case TR::Entity::INV_PUZZLE_2 : 1731 case TR::Entity::INV_PUZZLE_3 : 1732 case TR::Entity::INV_PUZZLE_4 : 1733 case TR::Entity::INV_KEY_ITEM_1 : 1734 case TR::Entity::INV_KEY_ITEM_2 : 1735 case TR::Entity::INV_KEY_ITEM_3 : 1736 case TR::Entity::INV_KEY_ITEM_4 : 1737 if (usedItem == item) 1738 return false; 1739 usedItem = item; 1740 break; 1741 case TR::Entity::INV_LEADBAR : 1742 for (int i = 0; i < level->entitiesCount; i++) { 1743 const TR::Entity &e = level->entities[i]; 1744 if (e.controller && e.type == TR::Entity::MIDAS_HAND) { 1745 MidasHand *controller = (MidasHand*)e.controller; 1746 if (controller->interaction) { 1747 controller->invItem = item; 1748 return false; // remove item from inventory 1749 } 1750 return true; 1751 } 1752 } 1753 return false; 1754 default : return false; 1755 } 1756 return true; 1757 } 1758 waterOutLara1759 bool waterOut() { 1760 // TODO: playSound 36 1761 if (collision.side != Collision::FRONT || pos.y - collision.info[Collision::FRONT].floor > 256 + 128) 1762 return false; 1763 1764 vec3 dst = pos + getDir() * (LARA_RADIUS + 32.0f); 1765 1766 TR::Level::FloorInfo info; 1767 getFloorInfo(getRoomIndex(), pos, info); 1768 int roomAbove = info.roomAbove; 1769 if (roomAbove == TR::NO_ROOM) 1770 return false; 1771 1772 getFloorInfo(roomAbove, dst, info); 1773 1774 int h = int(pos.y - info.floor); 1775 1776 if (h >= 0 && h <= (256 + 128) && (state == STATE_SURF_TREAD || animation.setState(STATE_SURF_TREAD)) && animation.setState(STATE_STOP)) { 1777 if ((level->version & TR::VER_VERSION) > TR::VER_TR1) { 1778 if (h < 128) // 0 clicks out of water animation 1779 animation.setAnim(ANIM_WATER_OUT); 1780 } 1781 alignToWall(LARA_RADIUS); 1782 roomIndex = roomAbove; 1783 pos.y = info.floor; 1784 specular = LARA_WET_SPECULAR; 1785 move(); 1786 return true; 1787 } 1788 1789 return false; 1790 } 1791 goUnderwaterLara1792 int goUnderwater() { 1793 angle.x = -PI * 0.25f; 1794 game->waterDrop(pos, 256.0f, 0.2f); 1795 stand = STAND_UNDERWATER; 1796 return animation.setAnim(ANIM_TO_UNDERWATER); 1797 } 1798 doPickUpLara1799 bool doPickUp() { 1800 if (!animation.canSetState(STATE_PICK_UP)) 1801 return false; 1802 1803 int room = getRoomIndex(); 1804 1805 pickupListCount = 0; 1806 1807 for (int i = 0; i < level->entitiesCount; i++) { 1808 TR::Entity &entity = level->entities[i]; 1809 if (!entity.controller || !entity.isPickup()) 1810 continue; 1811 1812 Controller *controller = (Controller*)entity.controller; 1813 1814 if (controller->getRoomIndex() != room || controller->flags.invisible) 1815 continue; 1816 1817 if (entity.type == TR::Entity::CRYSTAL) { 1818 if (Input::lastState[camera->cameraIndex] == cAction) { 1819 vec3 dir = controller->pos - pos; 1820 if (dir.length2() < SQR(350.0f) && getDir().dot(dir.normal()) > COS30) { 1821 pickupListCount = 0; 1822 game->invShow(camera->cameraIndex, Inventory::PAGE_SAVEGAME, i); 1823 return true; 1824 } 1825 } 1826 } else { 1827 if (!canPickup(controller)) 1828 continue; 1829 1830 ASSERT(pickupListCount < COUNT(pickupList)); 1831 pickupList[pickupListCount++] = controller; 1832 } 1833 } 1834 1835 if (pickupListCount > 0) { 1836 state = STATE_PICK_UP; 1837 return true; 1838 } 1839 1840 return false; 1841 } 1842 canPickupLara1843 bool canPickup(Controller *controller) { 1844 TR::Entity::Type type = controller->getEntity().type; 1845 1846 // get limits 1847 TR::Limits::Limit *limit; 1848 switch (type) { 1849 case TR::Entity::SCION_PICKUP_QUALOPEC : limit = &TR::Limits::SCION; break; 1850 case TR::Entity::SCION_PICKUP_HOLDER : limit = &TR::Limits::SCION_HOLDER; break; 1851 default : limit = level->rooms[getRoomIndex()].flags.water ? &TR::Limits::PICKUP_UNDERWATER : &TR::Limits::PICKUP; 1852 } 1853 1854 if (!checkInteraction(controller, limit, true)) 1855 return false; 1856 1857 if (stand == Character::STAND_UNDERWATER) 1858 angle.x = -25 * DEG2RAD; 1859 1860 // set new state 1861 switch (type) { 1862 case TR::Entity::SCION_PICKUP_QUALOPEC : 1863 animation.setAnim(level->models[TR::MODEL_LARA_SPEC].animation); 1864 camera->doCutscene(pos, angle.y); 1865 break; 1866 case TR::Entity::SCION_PICKUP_HOLDER : 1867 animation.setAnim(level->models[TR::MODEL_LARA_SPEC].animation); 1868 1869 angle = controller->angle; 1870 pos = controller->pos - vec3(0, -280, LARA_RADIUS + 512).rotateY(angle.y); 1871 1872 camera->doCutscene(pos, angle.y - PI * 0.5f); 1873 break; 1874 default : ; 1875 } 1876 1877 return true; 1878 } 1879 doTutorialLara1880 int doTutorial(int track) { 1881 if (level->version & TR::VER_TR1) 1882 switch (track) { // GYM tutorial routine 1883 case 28 : if (level->state.tracks[track].once && state == STATE_UP_JUMP) track = 29; break; 1884 case 37 : 1885 case 41 : if (state != STATE_HANG) return 0; break; 1886 case 42 : if (level->state.tracks[track].once && state == STATE_HANG) track = 43; break; 1887 case 49 : if (state != STATE_SURF_TREAD) return 0; break; 1888 case 50 : // end of GYM 1889 if (level->state.tracks[track].once) { 1890 timer += Core::deltaTime; 1891 if (timer > 3.0f) 1892 game->loadNextLevel(); 1893 } else { 1894 if (state != STATE_WATER_OUT) 1895 return 0; 1896 timer = 0.0f; 1897 } 1898 break; 1899 } 1900 return track; 1901 } 1902 1903 checkInteractionLara1904 bool checkInteraction(Controller *controller, const TR::Limits::Limit *limit, bool action) { 1905 if ((state != STATE_STOP && state != STATE_TREAD && state != STATE_PUSH_PULL_READY) || !action || !emptyHands()) 1906 return false; 1907 1908 vec3 tmpAngle = controller->angle; 1909 vec3 ctrlAngle = controller->angle; 1910 if (stand == STAND_UNDERWATER) 1911 ctrlAngle.x = -25 * DEG2RAD; 1912 if (!limit->alignAngle) 1913 ctrlAngle.y = angle.y; 1914 controller->angle = ctrlAngle; 1915 mat4 m = controller->getMatrix(); 1916 controller->angle = tmpAngle; 1917 1918 float fx = 0.0f; 1919 if (!limit->alignHoriz) 1920 fx = (m.transpose() * vec4(pos - controller->pos, 0.0f)).x; 1921 1922 vec3 targetPos = controller->pos + (m * vec4(fx, limit->dy, limit->dz, 0.0f)).xyz(); 1923 1924 vec3 deltaAbs = pos - targetPos; 1925 1926 vec3 deltaRel = (m.transpose() * vec4(pos - controller->pos, 0.0f)).xyz(); // inverse transform 1927 1928 // set item orientation to hack limits check 1929 if (limit->box.contains(deltaRel)) { 1930 float deltaAngY = shortAngle(angle.y, ctrlAngle.y); 1931 1932 if (stand == STAND_UNDERWATER) { 1933 float deltaAngX = shortAngle(angle.x, ctrlAngle.x); 1934 1935 if (deltaAbs.length() > 64.0f || max(fabs(deltaAngX), fabs(deltaAngY)) > (10.0f * DEG2RAD)) { 1936 pos -= deltaAbs.normal() * min(deltaAbs.length(), Core::deltaTime * 512.0f); 1937 angle.x += sign(deltaAngX) * min(fabsf(deltaAngX), Core::deltaTime * (90.0f * DEG2RAD)); 1938 angle.y += sign(deltaAngY) * min(fabsf(deltaAngY), Core::deltaTime * (90.0f * DEG2RAD)); 1939 return false; 1940 } 1941 } 1942 1943 if (fabsf(deltaAngY) <= limit->ay * DEG2RAD) { 1944 // align 1945 if (limit->alignAngle) 1946 angle = controller->angle; 1947 else 1948 angle.x = angle.z = 0.0f; 1949 1950 pos = targetPos; 1951 velocity = vec3(0.0f); 1952 speed = 0.0f; 1953 1954 return true; 1955 } 1956 } 1957 1958 return false; 1959 } 1960 refreshCameraLara1961 void refreshCamera(const TR::Level::FloorInfo &info) { 1962 const TR::FloorData::TriggerCommand *cameraCmdSwitch = NULL; 1963 const TR::FloorData::TriggerCommand *cameraCmdTarget = NULL; 1964 1965 int cmdIndex = 0; 1966 1967 while (cmdIndex < info.trigCmdCount) { 1968 const TR::FloorData::TriggerCommand &cmd = info.trigCmd[cmdIndex++]; 1969 1970 switch (cmd.action) { 1971 case TR::Action::CAMERA_SWITCH : 1972 ASSERT(!cameraCmdSwitch); 1973 cameraCmdSwitch = &cmd; 1974 cmdIndex++; // skip camera info cmd 1975 break; 1976 case TR::Action::CAMERA_TARGET : 1977 ASSERT(!cameraCmdTarget); 1978 cameraCmdTarget = &cmd; 1979 break; 1980 default : ; 1981 } 1982 } 1983 1984 if (cameraCmdTarget && camera->mode != Camera::MODE_LOOK && camera->mode != Camera::MODE_COMBAT) { 1985 camera->viewTarget = (Controller*)level->entities[cameraCmdTarget->args].controller; 1986 } 1987 1988 if (cameraCmdSwitch) { 1989 if (cameraCmdSwitch->args == camera->viewIndexLast) { 1990 camera->viewIndex = camera->viewIndexLast; 1991 1992 if (camera->mode == Camera::MODE_LOOK || camera->mode == Camera::MODE_COMBAT || camera->timer < 0) { 1993 camera->timer = -1.0f; 1994 camera->viewTarget = NULL; 1995 } else { 1996 camera->mode = Camera::MODE_STATIC; 1997 } 1998 } else { 1999 camera->viewTarget = NULL; 2000 } 2001 } else { 2002 if (viewTarget && camera->viewTarget != camera->viewTargetLast) { 2003 camera->viewTarget = NULL; 2004 } 2005 } 2006 } 2007 checkTriggerLara2008 void checkTrigger(Controller *controller, bool heavy) { 2009 TR::Level::FloorInfo info; 2010 getFloorInfo(controller->getRoomIndex(), controller->pos, info); 2011 2012 if (getEntity().isLara() && info.lava && info.floor == pos.y) { 2013 hit(LARA_MAX_HEALTH + 1, NULL, TR::HIT_LAVA); 2014 return; 2015 } 2016 2017 if (!info.trigCmdCount) return; // has no trigger 2018 2019 if (camera->mode != Camera::MODE_HEAVY) { 2020 refreshCamera(info); 2021 } 2022 2023 TR::Limits::Limit *limit = NULL; 2024 bool switchIsDown = false; 2025 float timer = info.trigInfo.timer == 1 ? EPS : float(info.trigInfo.timer); 2026 int cmdIndex = 0; 2027 int actionState = state; 2028 2029 switch (info.trigger) { 2030 case TR::Level::Trigger::ACTIVATE : break; 2031 2032 case TR::Level::Trigger::SWITCH : { 2033 Switch *controller = (Switch*)level->entities[info.trigCmd[cmdIndex++].args].controller; 2034 2035 if (controller->flags.state != TR::Entity::asActive) { 2036 limit = state == STATE_STOP ? (controller->getEntity().type == TR::Entity::SWITCH_BUTTON ? &TR::Limits::SWITCH_BUTTON : &TR::Limits::SWITCH) : &TR::Limits::SWITCH_UNDERWATER; 2037 if (checkInteraction(controller, limit, Input::state[camera->cameraIndex][cAction])) { 2038 actionState = (controller->state == Switch::STATE_DOWN && stand == STAND_GROUND) ? STATE_SWITCH_UP : STATE_SWITCH_DOWN; 2039 2040 int animIndex; 2041 switch (controller->getEntity().type) { 2042 case TR::Entity::SWITCH_BUTTON : animIndex = ANIM_PUSH_BUTTON; break; 2043 case TR::Entity::SWITCH_BIG : animIndex = controller->state == Switch::STATE_DOWN ? ANIM_SWITCH_BIG_UP : ANIM_SWITCH_BIG_DOWN; break; 2044 default : animIndex = -1; 2045 } 2046 2047 if (animation.setState(actionState, animIndex)) 2048 controller->activate(); 2049 } 2050 } 2051 2052 if (!controller->setTimer(timer)) 2053 return; 2054 2055 switchIsDown = controller->state == Switch::STATE_DOWN; 2056 break; 2057 } 2058 2059 case TR::Level::Trigger::KEY : { 2060 TR::Entity &entity = level->entities[info.trigCmd[cmdIndex++].args]; 2061 KeyHole *controller = (KeyHole*)entity.controller; 2062 2063 if (controller->flags.state == TR::Entity::asNone) { 2064 if (controller->flags.active == TR::ACTIVE || state != STATE_STOP) 2065 return; 2066 2067 actionState = entity.isPuzzleHole() ? STATE_USE_PUZZLE : STATE_USE_KEY; 2068 if (!animation.canSetState(actionState)) 2069 return; 2070 2071 limit = actionState == STATE_USE_PUZZLE ? &TR::Limits::PUZZLE_HOLE : &TR::Limits::KEY_HOLE; 2072 if (!checkInteraction(controller, limit, isPressed(ACTION) || usedItem != TR::Entity::NONE)) 2073 return; 2074 2075 if (usedItem == TR::Entity::NONE) { 2076 if (isPressed(ACTION) && !game->invChooseKey(camera->cameraIndex, entity.type)) 2077 game->playSound(TR::SND_NO, pos, Sound::PAN); // no compatible items in inventory 2078 return; 2079 } 2080 2081 if (TR::Level::convToInv(TR::Entity::getItemForHole(entity.type)) != usedItem) { // check compatibility if user select other 2082 game->playSound(TR::SND_NO, pos, Sound::PAN); // uncompatible item 2083 return; 2084 } 2085 2086 keyHole = controller; 2087 2088 if (game->invUse(camera->cameraIndex, usedItem)) { 2089 keyItem = game->addEntity(usedItem, getRoomIndex(), pos, 0); 2090 keyItem->lockMatrix = true; 2091 keyItem->pos = keyHole->pos + vec3(0, -590, 484).rotateY(-keyHole->angle.y); 2092 keyItem->angle.x = PI * 0.5f; 2093 keyItem->angle.y = keyHole->angle.y; 2094 } 2095 2096 animation.setState(actionState); 2097 } 2098 2099 if (controller->flags.state != TR::Entity::asInactive) 2100 return; 2101 2102 break; 2103 } 2104 2105 case TR::Level::Trigger::PICKUP : { 2106 Controller *controller = (Controller*)level->entities[info.trigCmd[cmdIndex++].args].controller; 2107 if (!controller->flags.invisible) 2108 return; 2109 break; 2110 } 2111 2112 case TR::Level::Trigger::COMBAT : 2113 if (wpnReady() && !emptyHands()) 2114 return; 2115 break; 2116 2117 case TR::Level::Trigger::PAD : 2118 case TR::Level::Trigger::ANTIPAD : 2119 if (pos.y != info.floor) return; 2120 break; 2121 2122 case TR::Level::Trigger::HEAVY : 2123 if (!heavy) return; 2124 break; 2125 case TR::Level::Trigger::DUMMY : 2126 return; 2127 } 2128 2129 bool needFlip = false; 2130 TR::Effect::Type effect = TR::Effect::NONE; 2131 2132 while (cmdIndex < info.trigCmdCount) { 2133 TR::FloorData::TriggerCommand &cmd = info.trigCmd[cmdIndex++]; 2134 2135 switch (cmd.action) { 2136 case TR::Action::ACTIVATE : { 2137 if (cmd.args >= level->entitiesBaseCount) { 2138 break; 2139 } 2140 TR::Entity &e = level->entities[cmd.args]; 2141 Controller *controller = (Controller*)e.controller; 2142 ASSERT(controller); 2143 TR::Entity::Flags &flags = controller->flags; 2144 2145 if (flags.once) 2146 break; 2147 controller->timer = timer; 2148 2149 if (info.trigger == TR::Level::Trigger::SWITCH) 2150 flags.active ^= info.trigInfo.mask; 2151 else if (info.trigger == TR::Level::Trigger::ANTIPAD) 2152 flags.active &= ~info.trigInfo.mask; 2153 else 2154 flags.active |= info.trigInfo.mask; 2155 2156 if (flags.active != TR::ACTIVE) 2157 break; 2158 2159 flags.once |= info.trigInfo.once; 2160 2161 controller->activate(); 2162 break; 2163 } 2164 case TR::Action::CAMERA_SWITCH : { 2165 TR::FloorData::TriggerCommand &cam = info.trigCmd[cmdIndex++]; 2166 2167 if (!level->cameras[cmd.args].flags.once) { 2168 camera->viewIndex = cmd.args; 2169 2170 if (!(info.trigger == TR::Level::Trigger::COMBAT) && 2171 !(info.trigger == TR::Level::Trigger::SWITCH && info.trigInfo.timer && !switchIsDown) && 2172 (info.trigger == TR::Level::Trigger::SWITCH || camera->viewIndex != camera->viewIndexLast)) 2173 { 2174 camera->smooth = cam.speed > 0; 2175 camera->mode = heavy ? Camera::MODE_HEAVY : Camera::MODE_STATIC; 2176 camera->timer = cam.timer == 1 ? EPS : float(cam.timer); 2177 camera->speed = cam.speed * 8; 2178 2179 level->cameras[camera->viewIndex].flags.once |= cam.once; 2180 } 2181 } 2182 break; 2183 } 2184 case TR::Action::FLOW : 2185 applyFlow(level->cameras[cmd.args]); 2186 break; 2187 case TR::Action::FLIP : { 2188 SaveState::ByteFlags &flip = level->state.flipmaps[cmd.args]; 2189 2190 if (flip.once) 2191 break; 2192 2193 if (info.trigger == TR::Level::Trigger::SWITCH) 2194 flip.active ^= info.trigInfo.mask; 2195 else 2196 flip.active |= info.trigInfo.mask; 2197 2198 if (flip.active == TR::ACTIVE) 2199 flip.once |= info.trigInfo.once; 2200 2201 if ((flip.active == TR::ACTIVE) ^ level->state.flags.flipped) 2202 needFlip = true; 2203 2204 break; 2205 } 2206 case TR::Action::FLIP_ON : 2207 if (level->state.flipmaps[cmd.args].active == TR::ACTIVE && !level->state.flags.flipped) 2208 needFlip = true; 2209 break; 2210 case TR::Action::FLIP_OFF : 2211 if (level->state.flipmaps[cmd.args].active == TR::ACTIVE && level->state.flags.flipped) 2212 needFlip = true; 2213 break; 2214 case TR::Action::CAMERA_TARGET : 2215 if (camera->mode == Camera::MODE_STATIC || camera->mode == Camera::MODE_HEAVY) { 2216 camera->viewTarget = (Controller*)level->entities[cmd.args].controller; 2217 } 2218 break; 2219 case TR::Action::END : 2220 game->loadNextLevel(); 2221 break; 2222 case TR::Action::SOUNDTRACK : { 2223 int track = doTutorial(cmd.args); 2224 2225 if (track == 0) break; 2226 2227 // check trigger 2228 SaveState::ByteFlags &flags = level->state.tracks[track]; 2229 2230 if (flags.once) 2231 break; 2232 2233 if (info.trigger == TR::Level::Trigger::SWITCH) 2234 flags.active ^= info.trigInfo.mask; 2235 else if (info.trigger == TR::Level::Trigger::ANTIPAD) 2236 flags.active &= ~info.trigInfo.mask; 2237 else 2238 flags.active |= info.trigInfo.mask; 2239 2240 if ( (flags.active == TR::ACTIVE) || (((level->version & (TR::VER_TR2 | TR::VER_TR3))) && flags.active) ) { 2241 flags.once |= info.trigInfo.once; 2242 game->playTrack(track); 2243 } else 2244 game->stopTrack(); 2245 2246 break; 2247 } 2248 case TR::Action::EFFECT : 2249 effect = TR::Effect::Type(cmd.args); 2250 break; 2251 case TR::Action::SECRET : 2252 if (!(saveStats.secrets & (1 << cmd.args))) { 2253 saveStats.secrets |= 1 << cmd.args; 2254 if (!game->playSound(TR::SND_SECRET, pos)) 2255 game->playTrack(TR::TRACK_TR1_SECRET, true); 2256 } 2257 break; 2258 case TR::Action::CLEAR_BODIES : 2259 break; 2260 case TR::Action::FLYBY : 2261 cmdIndex++; // TODO 2262 break; 2263 case TR::Action::CUTSCENE : 2264 cmdIndex++; // TODO 2265 break; 2266 } 2267 } 2268 2269 if (needFlip) { 2270 game->flipMap(); 2271 game->setEffect(this, effect); 2272 } 2273 } 2274 waterSplashLara2275 void waterSplash() { 2276 if (level->extra.waterSplash > -1) 2277 game->addEntity(TR::Entity::WATER_SPLASH, getRoomIndex(), vec3(pos.x, waterLevel, pos.z)); 2278 specular = LARA_WET_SPECULAR; 2279 } 2280 getStandLara2281 virtual Stand getStand() { 2282 if (dozy) return STAND_UNDERWATER; 2283 2284 if (stand == STAND_ONWATER && state == STATE_STOP) 2285 return STAND_GROUND; 2286 2287 if (state == STATE_HANG || state == STATE_HANG_LEFT || state == STATE_HANG_RIGHT) { 2288 if (input & ACTION) 2289 return STAND_HANG; 2290 animation.setAnim(ANIM_FALL_HANG); 2291 velocity = vec3(0.0f); 2292 pos.y += 128.0f; 2293 return STAND_AIR; 2294 } 2295 2296 if (state == STATE_HANDSTAND || (state == STATE_HANG_UP && animation.index != ANIM_CLIMB_JUMP)) 2297 return STAND_HANG; 2298 2299 if (getRoom().flags.water) { 2300 if (waterDepth > LARA_WADE_MAX_DEPTH || (waterLevel == 0.0f && waterDepth == 0.0f)) { 2301 if (stand == STAND_GROUND) 2302 return STAND_AIR; 2303 wpnHide(); 2304 if (stand == STAND_UNDERWATER || stand == STAND_ONWATER) 2305 return stand; 2306 if (stand == STAND_AIR) { 2307 if (velocity.y > 0.0f && pos.y - waterLevel > 300.0) { 2308 stopScreaming(); 2309 return STAND_UNDERWATER; 2310 } 2311 } else if (stand == STAND_GROUND) { 2312 return STAND_UNDERWATER; 2313 } else { 2314 pos.y = waterLevel; 2315 updateRoom(); 2316 switch (state) { 2317 case STATE_BACK : animation.setAnim(ANIM_ONWATER_SWIM_B); break; 2318 case STATE_STEP_LEFT : animation.setAnim(ANIM_ONWATER_SWIM_L); break; 2319 case STATE_STEP_RIGHT : animation.setAnim(ANIM_ONWATER_SWIM_R); break; 2320 default : animation.setAnim(ANIM_ONWATER_SWIM_F); 2321 } 2322 return STAND_ONWATER; 2323 } 2324 } else if (waterDepth > LARA_WADE_DEPTH) { 2325 if (stand == STAND_GROUND && (waterLevel + waterDepth) - pos.y > 300) 2326 return STAND_AIR; 2327 2328 if (stand == STAND_AIR) { 2329 if (velocity.y > 0.0f && pos.y - waterLevel > 300.0) { 2330 waterSplash(); 2331 pos.y = waterLevel + waterDepth; 2332 game->playSound(TR::SND_WATER_SPLASH, pos, Sound::PAN); 2333 updateRoom(); 2334 animation.setAnim(ANIM_SWIM_STAND); 2335 stopScreaming(); 2336 return STAND_WADE; 2337 } 2338 } else if (stand == STAND_UNDERWATER) { 2339 if (waterDepth <= LARA_SWIM_MIN_DEPTH) { 2340 pos.y = waterLevel + waterDepth; 2341 updateRoom(); 2342 animation.setAnim(ANIM_SWIM_STAND); 2343 return STAND_WADE; 2344 } 2345 } else if (stand == STAND_ONWATER || stand == STAND_GROUND) { 2346 if (state == STATE_RUN || state == STATE_SURF_SWIM) 2347 animation.setAnim(ANIM_WADE); 2348 pos.y = waterLevel + waterDepth; 2349 updateRoom(); 2350 return STAND_WADE; 2351 } else 2352 return STAND_WADE; 2353 } 2354 } 2355 2356 if (stand == STAND_ONWATER && state != STATE_STOP) { 2357 if (!getRoom().flags.water && state != STATE_WATER_OUT && state != STATE_STOP) 2358 return STAND_AIR; 2359 if (state != STATE_DIVE) 2360 return stand; 2361 } 2362 2363 if (stand == STAND_UNDERWATER || stand == STAND_ONWATER) { 2364 return stand; 2365 } 2366 2367 TR::Level::FloorInfo info; 2368 getFloorInfo(getRoomIndex(), pos, info); 2369 2370 if ((stand == STAND_SLIDE || stand == STAND_GROUND) && (state != STATE_FORWARD_JUMP && state != STATE_BACK_JUMP) && health > 0.0f) { 2371 if (pos.y + 8 >= info.floor && (abs(info.slantX) > 2 || abs(info.slantZ) > 2)) { 2372 pos.y = info.floor; 2373 2374 slide(); 2375 2376 return STAND_SLIDE; 2377 } 2378 } 2379 2380 int extra = (stand != STAND_AIR && stand != STAND_SLIDE) ? 256 : 0; 2381 2382 if (pos.y + extra >= info.floor && !(stand == STAND_AIR && velocity.y < 0)) { 2383 if (stand != STAND_GROUND) { 2384 pos.y = info.floor; 2385 // get damage from falling 2386 if (velocity.y > 0.0f) { 2387 stopScreaming(); 2388 if (state == STATE_FAST_DIVE && velocity.y > 133.0f) { 2389 hit(health + 1.0f, NULL, TR::HIT_FALL); 2390 } else { 2391 float v = velocity.y - 140.0f; 2392 if (v > 14.0f) 2393 hit(health + 1.0f, NULL, TR::HIT_FALL); 2394 else 2395 if (v > 0.0f) 2396 hit(v * v * LARA_MAX_HEALTH / 196.0f, NULL, TR::HIT_FALL); 2397 } 2398 2399 if (state == STATE_FALL && health > 0.0f) 2400 animation.setAnim(ANIM_LANDING); 2401 } 2402 } 2403 return STAND_GROUND; 2404 } 2405 2406 return STAND_AIR; 2407 } 2408 stopScreamingLara2409 void stopScreaming() { 2410 if (velocity.y >= 154.0f) 2411 Sound::stop(TR::SND_SCREAM); 2412 } 2413 getHeightLara2414 virtual int getHeight() { 2415 if (stand == STAND_GROUND || stand == STAND_AIR) 2416 return 768; 2417 return 0; 2418 } 2419 getStateAirLara2420 virtual int getStateAir() { 2421 angle.x = 0.0f; 2422 2423 float EPSILON = 3.0f; 2424 if (velocity.y > (131.0f + EPSILON) && state != STATE_SWAN_DIVE && state != STATE_FAST_DIVE) 2425 return STATE_FALL; 2426 2427 if (state == STATE_REACH && getDir().dot(vec3(velocity.x, 0.0f, velocity.z)) < 0) 2428 velocity.x = velocity.z = 0.0f; 2429 2430 if ((state == STATE_REACH || state == STATE_UP_JUMP) && (input & ACTION) && emptyHands()) { 2431 if (state == STATE_REACH && velocity.y < 0.0f) 2432 return state; 2433 2434 Box bounds = animation.getBoundingBox(pos, 0); 2435 2436 vec3 p = vec3(pos.x, bounds.min.y, pos.z); 2437 2438 Collision c = Collision(this, getRoomIndex(), p, getDir() * 128.0f, vec3(0.0f), LARA_RADIUS, angleExt, 0, 0, 0, 0); 2439 2440 if (c.side != Collision::FRONT) 2441 return state; 2442 2443 float floor = c.info[Collision::FRONT].floor; 2444 float ceiling = c.info[Collision::FRONT].ceiling; 2445 float hands = bounds.min.y; 2446 2447 if (fabsf(floor - hands) < 64 && int(floor) != int(ceiling)) { 2448 alignToWall(-LARA_RADIUS); 2449 pos.y = float(floor + LARA_HANG_OFFSET); 2450 stand = STAND_HANG; 2451 2452 if (state == STATE_REACH) { 2453 velocity = vec3(0.0f); 2454 TR::Level::FloorInfo info; 2455 getFloorInfo(getRoomIndex(), pos + getDir() * 256.0f, info); 2456 int h = int(info.ceiling - floor); 2457 return animation.setAnim((h > 0 && h < 400) ? ANIM_HANG_SWING : ANIM_HANG); 2458 } else 2459 return animation.setAnim(ANIM_HANG, -15); 2460 } 2461 } 2462 2463 if ((level->version & TR::VER_VERSION) > TR::VER_TR1) { 2464 bool roll = (input & (FORTH | BACK)) == (FORTH | BACK); 2465 2466 if ((state == STATE_FORWARD_JUMP && (roll || (input & BACK) )) || 2467 (state == STATE_BACK_JUMP && (roll || (input & FORTH) )) || 2468 (state == STATE_FAST_DIVE && roll)) 2469 return STATE_ROLL_AIR; 2470 } 2471 2472 if (state == STATE_FORWARD_JUMP || state == STATE_FALL_BACK) { 2473 if (emptyHands()) { 2474 if (input & ACTION) return STATE_REACH; 2475 if ((input & (JUMP | FORTH | WALK)) == (JUMP | FORTH | WALK)) return STATE_SWAN_DIVE; 2476 } 2477 } else 2478 if (state != STATE_FALL && state != STATE_FALL_BACK && state != STATE_SWAN_DIVE && state != STATE_FAST_DIVE && state != STATE_REACH && state != STATE_UP_JUMP && state != STATE_BACK_JUMP && state != STATE_LEFT_JUMP && state != STATE_RIGHT_JUMP) 2479 return animation.setAnim(ANIM_FALL_FORTH);// (state == STATE_FAST_BACK || state == STATE_SLIDE_BACK || state == STATE_ROLL_2) ? ANIM_FALL_BACK : ANIM_FALL_FORTH); 2480 2481 if (state == STATE_SWAN_DIVE) 2482 return STATE_FAST_DIVE; 2483 2484 return state; 2485 } 2486 entityQuadrantLara2487 int entityQuadrant(const TR::Entity &entity) { 2488 int ix = int(pos.x) / 1024; 2489 int iz = int(pos.z) / 1024; 2490 2491 int bx = entity.x / 1024; 2492 int bz = entity.z / 1024; 2493 2494 int q = -1; 2495 if (abs(bx - ix) ^ abs(bz - iz)) { 2496 if (bx > ix) q = 1; 2497 if (bx < ix) q = 3; 2498 if (bz > iz) q = 0; 2499 if (bz < iz) q = 2; 2500 } 2501 2502 return q; 2503 } 2504 getBlockLara2505 Block* getBlock() { 2506 for (int i = 0; i < level->entitiesCount; i++) { 2507 TR::Entity &e = level->entities[i]; 2508 if (!e.controller || !e.isBlock()) 2509 continue; 2510 2511 Block *block = (Block*)e.controller; 2512 float oldAngle = block->angle.y; 2513 block->angle.y = angleQuadrant(angle.y, 0.25f) * (PI * 0.5f); 2514 2515 if (!checkInteraction(block, &TR::Limits::BLOCK, (input & ACTION) != 0)) { 2516 block->angle.y = oldAngle; 2517 continue; 2518 } 2519 2520 return block; 2521 } 2522 return NULL; 2523 } 2524 getTurnLara2525 int getTurn() { 2526 if (state == STATE_FAST_TURN) 2527 return state; 2528 2529 bool weaponReady = wpnReady() && !emptyHands(); 2530 2531 if (input & LEFT) return (state == STATE_TURN_LEFT && (animation.prev == animation.index || weaponReady)) ? STATE_FAST_TURN : STATE_TURN_LEFT; 2532 if (input & RIGHT) return (state == STATE_TURN_RIGHT && (animation.prev == animation.index || weaponReady)) ? STATE_FAST_TURN : STATE_TURN_RIGHT; 2533 2534 ASSERT(false); 2535 return STATE_STOP; 2536 } 2537 checkClimbLara2538 bool checkClimb() { 2539 if ((input & (FORTH | ACTION)) == (FORTH | ACTION) && !(input & (LEFT | RIGHT)) && (animation.index == ANIM_STAND || animation.index == ANIM_STAND_NORMAL) && emptyHands() && collision.side == Collision::FRONT) { // TODO: get rid of animation.index 2540 float floor = collision.info[Collision::FRONT].floor; 2541 float ceiling = collision.info[Collision::FRONT].ceiling; 2542 2543 float h = pos.y - floor; 2544 2545 int aIndex = animation.index; 2546 bool canClimb = (floor - ceiling >= LARA_HEIGHT) && (h >= 256); 2547 2548 if (canClimb && h <= 2 * 256 + 128) { 2549 aIndex = ANIM_CLIMB_2; 2550 pos.y = floor + 512.0f; 2551 } else if (canClimb && h <= 3 * 256 + 128) { 2552 aIndex = ANIM_CLIMB_3; 2553 pos.y = floor + 768.0f; 2554 } else if (h > 3 * 128 + 128 && h <= 7 * 256 + 128) 2555 aIndex = ANIM_CLIMB_JUMP; 2556 2557 if (aIndex != animation.index) { 2558 alignToWall(-LARA_RADIUS); 2559 animation.setAnim(aIndex); 2560 return true; 2561 } 2562 } 2563 return false; 2564 } 2565 getStateGroundLara2566 virtual int getStateGround() { 2567 int res = STATE_STOP; 2568 angle.x = 0.0f; 2569 2570 if (waterDepth > 0.0f && !(animation.frameIndex % 4)) 2571 game->waterDrop(getJoint(jointChest).pos + vec3(randf(), 0.0f, randf()) * 64.0f, 96.0f, 0.02f); 2572 2573 if ((input == ACTION) && (state == STATE_STOP) && emptyHands() && doPickUp()) 2574 return state; 2575 2576 if (checkClimb()) 2577 return state; 2578 2579 if ( (input & (FORTH | BACK)) == (FORTH | BACK) && (animation.index == ANIM_STAND_NORMAL || state == STATE_RUN) ) 2580 return animation.setAnim(ANIM_STAND_ROLL_BEGIN); 2581 2582 // ready to jump 2583 if (state == STATE_COMPRESS) { 2584 float ext = angle.y; 2585 switch (input & (RIGHT | LEFT | FORTH | BACK)) { 2586 case RIGHT : res = STATE_RIGHT_JUMP; ext += PI * 0.5f; break; 2587 case LEFT : res = STATE_LEFT_JUMP; ext -= PI * 0.5f; break; 2588 case FORTH | LEFT : 2589 case FORTH | RIGHT : 2590 case FORTH : res = STATE_FORWARD_JUMP; break; 2591 case BACK : res = STATE_BACK_JUMP; ext += PI; break; 2592 default : res = STATE_UP_JUMP; break; 2593 } 2594 2595 if (res != STATE_UP_JUMP) { 2596 vec3 p = pos; 2597 collision = Collision(this, getRoomIndex(), p, vec3(0.0f), vec3(0.0f), LARA_RADIUS * 2.5f, ext, 0, LARA_HEIGHT, 256 + 128, 0xFFFFFF); 2598 if (collision.side == Collision::FRONT) 2599 res = STATE_UP_JUMP; 2600 } 2601 2602 return res; 2603 } 2604 2605 if (state == STATE_RUN) { 2606 if (animation.index == ANIM_RUN_START) { 2607 canJump = false; 2608 } else if (animation.index == ANIM_RUN) { 2609 if (animation.frameIndex >= 4 && animation.frameIndex <= 5) { 2610 canJump = true; 2611 } 2612 } else { 2613 canJump = true; 2614 } 2615 } 2616 2617 // jump button is pressed 2618 if (input & JUMP) { 2619 if ((input & FORTH) && state == STATE_FORWARD_JUMP) 2620 return STATE_RUN; 2621 if (state == STATE_RUN && canJump) 2622 return STATE_FORWARD_JUMP; 2623 if (animation.index == ANIM_SLIDE_BACK) // TODO: animation index? %) 2624 return STATE_SLIDE_BACK; 2625 return STATE_COMPRESS; 2626 } 2627 2628 // walk button is pressed 2629 if ((input & WALK) && animation.index != ANIM_RUN_START) { 2630 float ext = angle.y; 2631 2632 if (input & FORTH) { 2633 if (state == STATE_BACK) 2634 res = STATE_STOP; 2635 else 2636 res = STATE_WALK; 2637 } else if (input & BACK) { 2638 res = STATE_BACK; 2639 ext += PI; 2640 } else if (input & LEFT) { 2641 res = STATE_STEP_LEFT; 2642 ext -= PI * 0.5f; 2643 } else if (input & RIGHT) { 2644 res = STATE_STEP_RIGHT; 2645 ext += PI * 0.5f; 2646 } 2647 2648 int maxAscent = 256 + 128; 2649 int maxDescent = maxAscent; 2650 2651 if (state == STATE_STEP_LEFT || state == STATE_STEP_RIGHT) 2652 maxAscent = maxDescent = 64; 2653 2654 if (state == STATE_STOP && res != STATE_STOP) 2655 res = checkMove(res, maxAscent, maxDescent); 2656 return res; 2657 } 2658 2659 if ((input & ACTION) && emptyHands()) { 2660 if (state == STATE_PUSH_PULL_READY && (input & (FORTH | BACK))) { 2661 int pushState = (input & FORTH) ? STATE_PUSH_BLOCK : STATE_PULL_BLOCK; 2662 Block *block = getBlock(); 2663 if (block && animation.canSetState(pushState) && block->doMove((input & FORTH) != 0)) 2664 return pushState; 2665 } 2666 2667 if (state == STATE_PUSH_PULL_READY || getBlock()) 2668 return STATE_PUSH_PULL_READY; 2669 } 2670 2671 // only dpad buttons pressed 2672 if (input & FORTH) 2673 res = STATE_RUN; 2674 else if (input & BACK) 2675 res = STATE_FAST_BACK; 2676 else if (input & (LEFT | RIGHT)) 2677 return getTurn(); 2678 2679 if (state == STATE_STOP && res != STATE_STOP) { 2680 float ext = angle.y + (res == STATE_RUN ? 0.0f : PI); 2681 vec3 p = pos; 2682 collision = Collision(this, getRoomIndex(), p, vec3(0.0f), vec3(0.0f), LARA_RADIUS * 1.1f, ext, 0, LARA_HEIGHT, 256 + 128, 0xFFFFFF); 2683 if (collision.side == Collision::FRONT) 2684 res = STATE_STOP; 2685 } 2686 2687 return res; 2688 } 2689 slideLara2690 void slide() { 2691 TR::Level::FloorInfo info; 2692 getFloorInfo(getRoomIndex(), pos, info); 2693 2694 int sx = abs(info.slantX), sz = abs(info.slantZ); 2695 // get direction 2696 float dir; 2697 if (sx >= sz) 2698 dir = info.slantX > 0 ? 3.0f : 1.0f; 2699 else 2700 dir = info.slantZ > 0 ? 2.0f : 0.0f; 2701 dir *= PI * 0.5f; 2702 2703 int aIndex = ANIM_SLIDE_FORTH; 2704 if (fabsf(shortAngle(dir, angle.y)) > PI * 0.5f) { 2705 aIndex = ANIM_SLIDE_BACK; 2706 dir += PI; 2707 } 2708 2709 angle.y = dir; 2710 if (animation.index != aIndex) 2711 animation.setAnim(aIndex); 2712 } 2713 getStateSlideLara2714 virtual int getStateSlide() { 2715 if (input & JUMP) 2716 return state == STATE_SLIDE ? STATE_FORWARD_JUMP : STATE_BACK_JUMP; 2717 // TODO: update slide direction 2718 return state; 2719 } 2720 getStateHangLara2721 virtual int getStateHang() { 2722 if (input & LEFT) return STATE_HANG_LEFT; 2723 if (input & RIGHT) return STATE_HANG_RIGHT; 2724 if (input & FORTH) { 2725 // possibility check 2726 TR::Level::FloorInfo info; 2727 getFloorInfo(getRoomIndex(), pos + getDir() * (LARA_RADIUS + 2.0f), info); 2728 if (info.floor - info.ceiling >= LARA_HEIGHT) 2729 return (input & WALK) ? STATE_HANDSTAND : STATE_HANG_UP; 2730 } 2731 return STATE_HANG; 2732 } 2733 getStateUnderwaterLara2734 virtual int getStateUnderwater() { 2735 if ((input == ACTION) && (state == STATE_TREAD) && emptyHands() && doPickUp()) 2736 return state; 2737 2738 if (state == STATE_RUN || state == STATE_BACK || state == STATE_FAST_BACK || state == STATE_FORWARD_JUMP || state == STATE_UP_JUMP || state == STATE_BACK_JUMP || state == STATE_LEFT_JUMP || state == STATE_RIGHT_JUMP || state == STATE_FALL || state == STATE_REACH || state == STATE_SLIDE || state == STATE_SLIDE_BACK) { 2739 game->waterDrop(pos, 256.0f, 0.2f); 2740 waterSplash(); 2741 pos.y += 100.0f; 2742 angle.x = -45.0f * DEG2RAD; 2743 velocity.y *= 1.5f; 2744 return animation.setAnim(ANIM_WATER_FALL); // TODO: wronng animation 2745 } 2746 2747 if ((level->version & TR::VER_VERSION) > TR::VER_TR1 && state != STATE_ROLL_WATER) { 2748 if ((input & (FORTH | BACK)) == (FORTH | BACK)) 2749 return animation.setAnim(ANIM_ROLL_WATER); 2750 } 2751 2752 if (state == STATE_SWAN_DIVE || state == STATE_FAST_DIVE) { 2753 angle.x = (state == STATE_SWAN_DIVE ? -45.0f : -85.0f) * DEG2RAD; 2754 pos.y += 100.0f; 2755 velocity.y *= 2.0f; 2756 game->waterDrop(pos, 128.0f, 0.2f); 2757 waterSplash(); 2758 return STATE_DIVE; 2759 } 2760 2761 if (input & JUMP) return STATE_SWIM; 2762 2763 if (state == STATE_GLIDE && speed < LARA_SWIM_SPEED * 2.0f / 3.0f) 2764 return STATE_TREAD; 2765 2766 return STATE_GLIDE; 2767 } 2768 getStateOnwaterLara2769 virtual int getStateOnwater() { 2770 angle.x = 0.0f; 2771 2772 if (state == STATE_WATER_OUT) return state; 2773 2774 if (state != STATE_SURF_TREAD && state != STATE_SURF_LEFT && state != STATE_SURF_RIGHT && state != STATE_SURF_SWIM && state != STATE_SURF_BACK && state != STATE_STOP) { 2775 game->waterDrop(pos, 128.0f, 0.2f); 2776 specular = LARA_WET_SPECULAR; 2777 switch (state) { 2778 case STATE_WADE : return animation.setAnim(ANIM_ONWATER_SWIM_F); 2779 case STATE_BACK : return animation.setAnim(ANIM_ONWATER_SWIM_B); 2780 case STATE_STEP_LEFT : return animation.setAnim(ANIM_ONWATER_SWIM_L); 2781 case STATE_STEP_RIGHT : return animation.setAnim(ANIM_ONWATER_SWIM_R); 2782 default : return animation.setAnim(ANIM_TO_ONWATER); 2783 } 2784 } 2785 2786 if (state == STATE_SURF_TREAD) { 2787 if (animation.isFrameActive(0)) 2788 game->waterDrop(getJoint(jointHead).pos, 96.0f, 0.03f); 2789 } else { 2790 if (animation.frameIndex % 4 == 0) 2791 game->waterDrop(getJoint(jointHead).pos, 96.0f, 0.02f); 2792 } 2793 2794 if (input & FORTH) { 2795 if (input & JUMP) 2796 return goUnderwater(); 2797 2798 if ((input & ACTION) && waterOut()) { 2799 game->waterDrop(pos, 128.0f, 0.2f); 2800 return state; 2801 } 2802 2803 return STATE_SURF_SWIM; 2804 } 2805 2806 if (input & BACK) return STATE_SURF_BACK; 2807 if (input & WALK) { 2808 if (input & LEFT) return STATE_SURF_LEFT; 2809 if (input & RIGHT) return STATE_SURF_RIGHT; 2810 } 2811 return STATE_SURF_TREAD; 2812 } 2813 getLeadingFootLara2814 bool getLeadingFoot() { 2815 int rightStart = 0; 2816 if (state == STATE_RUN) rightStart = 6; 2817 if (state == STATE_WALK) rightStart = 13; 2818 if (state == STATE_BACK) rightStart = 28; 2819 return animation.frameIndex < rightStart || animation.frameIndex > (rightStart + animation.framesCount / 2); 2820 } 2821 2822 int checkMove(int newState, int maxAscent = 256 + 128, int maxDescent = 0xFFFFFF) { 2823 float ext = angle.y; 2824 if (newState == STATE_BACK || newState == STATE_FAST_BACK) 2825 ext += PI; 2826 else if (newState == STATE_STEP_LEFT) 2827 ext -= PI * 0.5f; 2828 else if (newState == STATE_STEP_RIGHT) 2829 ext += PI * 0.5f; 2830 vec3 p = pos; 2831 collision = Collision(this, getRoomIndex(), p, vec3(0.0f), vec3(0.0f), LARA_RADIUS * 1.1f, ext, 0, LARA_HEIGHT, maxAscent, maxDescent); 2832 if (collision.side == Collision::FRONT) 2833 return STATE_STOP; 2834 return newState; 2835 } 2836 getStateWadeLara2837 virtual int getStateWade() { 2838 angle.x = 0.0f; 2839 2840 if (waterDepth > 0.0f && !(animation.frameIndex % 4)) 2841 game->waterDrop(getJoint(jointChest).pos + vec3(randf(), 0.0f, randf()) * 64.0f, 96.0f, 0.02f); 2842 2843 if ((input & FORTH) && (input & BACK)) 2844 input &= ~(FORTH | BACK); 2845 2846 if (checkClimb()) 2847 return state; 2848 2849 if (input & JUMP) { 2850 return STATE_COMPRESS; 2851 } 2852 2853 if (state == STATE_COMPRESS || state == STATE_UP_JUMP) 2854 return state; 2855 2856 if (state != STATE_WADE) { 2857 if (state == STATE_SWIM || state == STATE_GLIDE) 2858 return animation.setAnim(ANIM_WADE_ASCEND); 2859 if (state == STATE_COMPRESS) 2860 return STATE_UP_JUMP; 2861 if (state == STATE_RUN) { 2862 waterSplash(); 2863 return animation.setAnim(getLeadingFoot() ? ANIM_WADE_RUN_LEFT : ANIM_WADE_RUN_RIGHT); 2864 } 2865 if (state == STATE_SURF_SWIM) 2866 return animation.setAnim(ANIM_WADE_SWIM); 2867 if (state == STATE_SURF_BACK) 2868 return animation.setAnim(ANIM_BACK); 2869 } 2870 2871 if (input & FORTH) { 2872 if (checkMove(STATE_WADE) != STATE_WADE) 2873 return STATE_STOP; 2874 if (state == STATE_STOP) 2875 return animation.setAnim(ANIM_WADE_STAND); 2876 if (state != STATE_WADE && state != STATE_WATER_OUT) 2877 return animation.setAnim(ANIM_WADE); 2878 return STATE_WADE; 2879 } 2880 2881 if (input & BACK) { 2882 if (checkMove(STATE_BACK) != STATE_BACK) 2883 return STATE_STOP; 2884 return STATE_BACK; 2885 } 2886 2887 // walk button is pressed 2888 if ((input & WALK) && (input & (LEFT | RIGHT)) && animation.index != ANIM_RUN_START) { 2889 int res; 2890 2891 float ext = angle.y; 2892 2893 if (input & LEFT) { 2894 res = STATE_STEP_LEFT; 2895 ext -= PI * 0.5f; 2896 } else if (input & RIGHT) { 2897 res = STATE_STEP_RIGHT; 2898 ext += PI * 0.5f; 2899 } 2900 2901 int maxAscent = 256 + 128; 2902 int maxDescent = maxAscent; 2903 2904 if (state == STATE_STEP_LEFT || state == STATE_STEP_RIGHT) 2905 maxAscent = maxDescent = 64; 2906 2907 if (state == STATE_STOP && res != STATE_STOP) 2908 res = checkMove(res, maxAscent, maxDescent); 2909 return res; 2910 } 2911 2912 if (input & (LEFT | RIGHT)) 2913 return getTurn(); 2914 2915 return STATE_STOP; 2916 } 2917 getStateDeathLara2918 virtual int getStateDeath() { 2919 if (stand == STAND_UNDERWATER || stand == STAND_ONWATER) 2920 return STATE_UNDERWATER_DEATH; 2921 if (state == STATE_MIDAS_DEATH) 2922 return STATE_MIDAS_DEATH; 2923 if (state == STATE_HANG || state == STATE_HANG_LEFT || state == STATE_HANG_RIGHT || state == STATE_UP_JUMP) 2924 return STATE_FALL; 2925 return STATE_DEATH; 2926 } 2927 getStateDefaultLara2928 virtual int getStateDefault() { 2929 if (state == STATE_DIVE || (state == STATE_RUN && (input & JUMP)) ) return state; 2930 switch (stand) { 2931 case STAND_GROUND : return STATE_STOP; 2932 case STAND_HANG : return STATE_HANG; 2933 case STAND_ONWATER : return STATE_SURF_TREAD; 2934 case STAND_UNDERWATER : return STATE_TREAD; 2935 case STAND_WADE : return STATE_STOP; 2936 default : ; 2937 } 2938 return STATE_FALL; 2939 } 2940 updateStateLara2941 virtual void updateState() { 2942 Character::updateState(); 2943 2944 if (camera->mode != ICamera::MODE_FOLLOW) 2945 return; 2946 2947 camera->centerView = false; 2948 2949 switch (state) { 2950 case STATE_WATER_OUT : 2951 camera->centerView = true; 2952 break; 2953 case STATE_DEATH : 2954 case STATE_UNDERWATER_DEATH : 2955 camera->centerView = true; 2956 break; 2957 case STATE_REACH : 2958 camera->setAngle(0, 85); 2959 break; 2960 case STATE_BACK_JUMP : 2961 camera->setAngle(0, 135); 2962 break; 2963 case STATE_SURF_TREAD : 2964 case STATE_SURF_SWIM : 2965 case STATE_SURF_BACK : 2966 case STATE_SURF_LEFT : 2967 case STATE_SURF_RIGHT : 2968 camera->setAngle(-22, 0); 2969 break; 2970 case STATE_SLIDE : 2971 case STATE_SLIDE_BACK : 2972 camera->setAngle(-45, 0); 2973 break; 2974 case STATE_HANG : 2975 case STATE_HANG_LEFT : 2976 case STATE_HANG_RIGHT : 2977 camera->setAngle(-60, 0); 2978 break; 2979 case STATE_PUSH_BLOCK : 2980 case STATE_PULL_BLOCK : 2981 camera->setAngle(-25, 35); 2982 camera->centerView = true; 2983 break; 2984 case STATE_PUSH_PULL_READY : 2985 camera->setAngle(0, 75); 2986 break; 2987 case STATE_PICK_UP : 2988 camera->setAngle(-15, -130); 2989 break; 2990 case STATE_SWITCH_DOWN : 2991 case STATE_SWITCH_UP : 2992 camera->setAngle(-25, 80); 2993 break; 2994 case STATE_USE_KEY : 2995 case STATE_USE_PUZZLE : 2996 camera->setAngle(-25, -80); 2997 break; 2998 case STATE_SPECIAL : 2999 camera->setAngle(-25, 170); 3000 camera->centerView = true; 3001 break; 3002 case STATE_WADE : 3003 camera->setAngle(-22, 0); 3004 break; 3005 default : 3006 camera->setAngle(0, 0); 3007 } 3008 } 3009 setDozyLara3010 void setDozy(bool enable) { 3011 if (enable) { 3012 reset(getRoomIndex(), pos - vec3(0, 512, 0), angle.y, STAND_UNDERWATER); 3013 } else { 3014 stand = getRoom().flags.water ? STAND_UNDERWATER : STAND_AIR; 3015 } 3016 dozy = enable; 3017 } 3018 getInputLara3019 virtual int getInput() { // TODO: updateInput 3020 if (level->isCutsceneLevel()) return 0; 3021 3022 if (networkInput != -1) 3023 return networkInput; 3024 3025 input = 0; 3026 int pid = camera->cameraIndex; 3027 3028 if (!dozy && ((Input::state[pid][cAction] && Input::state[pid][cJump] && Input::state[pid][cLook] && Input::state[pid][cDash]) || Input::down[ikO])) { 3029 setDozy(true); 3030 return input; 3031 } 3032 3033 if (dozy && Input::state[pid][cWalk]) { 3034 setDozy(false); 3035 return input; 3036 } 3037 3038 input = Character::getInput(); 3039 if (input & DEATH) { 3040 if (stand != STAND_AIR) // Lara can't die in the air 3041 return input; 3042 input &= ~DEATH; 3043 } 3044 3045 if (Input::state[pid][cUp]) input |= FORTH; 3046 if (Input::state[pid][cRight]) input |= RIGHT; 3047 if (Input::state[pid][cDown]) input |= BACK; 3048 if (Input::state[pid][cLeft]) input |= LEFT; 3049 if (Input::state[pid][cRoll]) input = FORTH | BACK; 3050 if (Input::state[pid][cJump]) input |= JUMP; 3051 if (Input::state[pid][cWalk]) input |= WALK; 3052 if (Input::state[pid][cAction]) input |= ACTION; 3053 if (Input::state[pid][cWeapon]) input |= WEAPON; 3054 if (Input::state[pid][cLook] && canLookAt()) input = LOOK; 3055 //if (Input::state[pid][cStepRight]) input = WALK | RIGHT; 3056 //if (Input::state[pid][cStepLeft]) input = WALK | LEFT; 3057 3058 3059 if (Input::down[ikP]) { 3060 switch (level->id) { 3061 case TR::LVL_TR1_GYM : 3062 reset(14, vec3(40448, 3584, 60928), PI * 0.5f, STAND_ONWATER); // gym (pool) 3063 break; 3064 case TR::LVL_TR1_1 : 3065 reset(33, vec3(48229, 4608, 78420), 270 * DEG2RAD); // level 1 (end) 3066 break; 3067 case TR::LVL_TR1_2 : 3068 reset(61, vec3(21987, -1024, 29144), PI * 3.0f * 0.5f); // level 2 (trap door) 3069 break; 3070 case TR::LVL_TR1_3A : 3071 reset(51, vec3(41015, 3584, 34494), -PI); // level 3a (t-rex) 3072 break; 3073 case TR::LVL_TR1_3B : 3074 reset(5, vec3(73394, 3840, 60758), 0); // level 3b (scion) 3075 break; 3076 case TR::LVL_TR1_4 : 3077 reset(18, vec3(34914, 11008, 41315), 90 * DEG2RAD); // main hall 3078 break; 3079 case TR::LVL_TR1_5 : 3080 reset(74, vec3(52549, -3584, 60871), -150 * DEG2RAD); // main hall 3081 break; 3082 case TR::LVL_TR1_6 : 3083 reset(73, vec3(73372, 122, 51687), PI * 0.5f); // level 6 (midas hand) 3084 break; 3085 case TR::LVL_TR1_7A : 3086 reset(99, vec3(45562, -3328, 63366), 225 * DEG2RAD); // level 7a (flipmap) 3087 break; 3088 case TR::LVL_TR1_7B : 3089 // reset(77, vec3(36943, -4096, 62821), 270 * DEG2RAD); // level 7b (heavy trigger) 3090 reset(49, vec3(45596, -6144, 71579), 0); // level 7b (water trigger) 3091 break; 3092 case TR::LVL_TR2_WALL : 3093 //reset(44, vec3(62976, 1536, 23040), 0); 3094 //reset(44, vec3(62976, 1536, 23040), 0); 3095 break; 3096 case TR::LVL_TR2_PLATFORM : 3097 reset(16, vec3(53029, -5120, 77359), 0); 3098 break; 3099 case TR::LVL_TR3_TEMPLE : 3100 reset(204, vec3(40562, 3584, 58694), 0); 3101 break; 3102 default : game->playSound(TR::SND_NO, pos, Sound::PAN); 3103 } 3104 } 3105 3106 // analog control 3107 rotFactor = vec2(1.0f); 3108 3109 if (input & LOOK) 3110 return input; 3111 3112 if (camera->spectator) 3113 return input; 3114 3115 Input::Joystick &joy = Input::joy[Core::settings.controls[pid].joyIndex]; 3116 3117 vec2 L = joy.L; 3118 3119 if (L.length() < INPUT_JOY_DZ_STICK) L = vec2(0.0f); // dead zone 3120 3121 if (!((state == STATE_STOP || state == STATE_SURF_TREAD || state == STATE_HANG) && fabsf(L.x) < 0.5f && fabsf(L.y) < 0.5f)) { 3122 bool moving = state == STATE_RUN || state == STATE_WALK || state == STATE_BACK || state == STATE_FAST_BACK || state == STATE_SURF_SWIM || state == STATE_SURF_BACK || state == STATE_FORWARD_JUMP; 3123 3124 if (!moving) { 3125 if (fabsf(L.x) < fabsf(L.y)) 3126 L.x = 0.0f; 3127 else 3128 L.y = 0.0f; 3129 } 3130 3131 if (L.x != 0.0f) { 3132 input |= (L.x < 0.0f) ? LEFT : RIGHT; 3133 if (moving || stand == STAND_UNDERWATER || stand == STAND_ONWATER) 3134 rotFactor.y = min(fabsf(L.x) / 0.9f, 1.0f); 3135 } 3136 3137 if (L.y != 0.0f) { 3138 input |= (L.y < 0.0f) ? FORTH : BACK; 3139 if (stand == STAND_UNDERWATER) 3140 rotFactor.x = min(fabsf(L.y) / 0.9f, 1.0f); 3141 } 3142 } 3143 3144 // VR control 3145 if (Core::settings.detail.stereo == Core::Settings::STEREO_VR && camera->firstPerson && canFreeRotate()) { 3146 3147 if (!(input & WALK)) { 3148 input &= ~(LEFT | RIGHT); 3149 } 3150 3151 vec3 ang = getAngleAbs(Input::hmd.head.dir().xyz()); 3152 angle.y = ang.y; 3153 if (stand == STAND_UNDERWATER) { 3154 input &= ~(FORTH | BACK); 3155 angle.x = ang.x; 3156 } 3157 } 3158 return input; 3159 } 3160 useHeadAnimationLara3161 virtual bool useHeadAnimation() { 3162 return state == STATE_WATER_OUT 3163 || state == STATE_DEATH 3164 || state == STATE_UNDERWATER_DEATH 3165 || state == STATE_HANG 3166 || state == STATE_HANG_UP 3167 || state == STATE_HANG_LEFT 3168 || state == STATE_HANG_RIGHT 3169 || state == STATE_PUSH_BLOCK 3170 || state == STATE_PULL_BLOCK 3171 || state == STATE_PUSH_PULL_READY 3172 || state == STATE_PICK_UP 3173 || state == STATE_SWITCH_DOWN 3174 || state == STATE_SWITCH_UP 3175 || state == STATE_USE_KEY 3176 || state == STATE_USE_PUZZLE 3177 || state == STATE_SPECIAL 3178 || state == STATE_REACH 3179 || state == STATE_SWAN_DIVE 3180 || state == STATE_FAST_DIVE 3181 || state == STATE_HANDSTAND 3182 || state == STATE_ROLL_START 3183 || state == STATE_ROLL_END 3184 || state == STATE_MIDAS_USE 3185 || state == STATE_MIDAS_DEATH 3186 || animation.index == ANIM_CLIMB_2 3187 || animation.index == ANIM_CLIMB_3 3188 || animation.index == ANIM_CLIMB_JUMP; 3189 } 3190 canFreeRotateLara3191 bool canFreeRotate() { 3192 return !(useHeadAnimation() || state == STATE_SLIDE || state == STATE_SLIDE_BACK); 3193 } 3194 doCustomCommandLara3195 virtual void doCustomCommand(int curFrame, int prevFrame) { 3196 switch (state) { 3197 case STATE_PICK_UP : { 3198 int pickupFrame = stand == STAND_GROUND ? PICKUP_FRAME_GROUND : PICKUP_FRAME_UNDERWATER; 3199 if (animation.isFrameActive(pickupFrame)) { 3200 camera->setup(true); 3201 3202 for (int i = 0; i < pickupListCount; i++) { 3203 Controller *item = pickupList[i]; 3204 3205 if (item->getEntity().type == TR::Entity::SCION_PICKUP_HOLDER) 3206 continue; 3207 item->deactivate(); 3208 item->flags.invisible = true; 3209 game->invAdd(item->getEntity().type, 1); 3210 3211 vec4 p = game->projectPoint(vec4(item->pos, 1.0f)); 3212 3213 if (p.w != 0.0f) { 3214 p.x = ( p.x / p.w * 0.5f + 0.5f) * UI::width; 3215 p.y = (-p.y / p.w * 0.5f + 0.5f) * UI::height; 3216 if (game->getLara(1)) { 3217 p.x *= 0.5f; 3218 } 3219 } else 3220 p = vec4(UI::width * 0.5f, UI::height * 0.5f, 0.0f, 0.0f); 3221 3222 UI::addPickup(item->getEntity().type, camera->cameraIndex, vec2(p.x, p.y)); 3223 saveStats.pickups++; 3224 } 3225 pickupListCount = 0; 3226 } 3227 break; 3228 } 3229 case STATE_USE_KEY : 3230 case STATE_USE_PUZZLE : { 3231 if (keyHole) { 3232 if (animation.isFrameActive(state == STATE_USE_PUZZLE ? PUZZLE_FRAME : KEY_FRAME)) { 3233 keyHole->activate(); 3234 if (keyItem) { 3235 if (state == STATE_USE_KEY) 3236 keyItem->lockMatrix = false; 3237 else 3238 game->removeEntity(keyItem); 3239 } 3240 3241 keyItem = NULL; 3242 keyHole = NULL; 3243 } 3244 } 3245 break; 3246 } 3247 } 3248 } 3249 updateLara3250 virtual void update() { 3251 if (Input::state[camera->cameraIndex][cLook] && Input::lastState[camera->cameraIndex] == cAction) 3252 camera->changeView(!camera->firstPerson); 3253 3254 if (level->isCutsceneLevel()) { 3255 updateAnimation(true); 3256 3257 updateLights(); 3258 3259 if (fixRoomIndex()) { 3260 for (int i = 0; i < COUNT(braid); i++) { 3261 if (braid[i]) { 3262 braid[i]->update(); 3263 } 3264 } 3265 } 3266 } else { 3267 switch (usedItem) { 3268 case TR::Entity::INV_MEDIKIT_SMALL : 3269 case TR::Entity::INV_MEDIKIT_BIG : 3270 if (health < LARA_MAX_HEALTH) { 3271 damageTime = LARA_DAMAGE_TIME; 3272 health = min(LARA_MAX_HEALTH, health + (usedItem == TR::Entity::INV_MEDIKIT_SMALL ? LARA_MAX_HEALTH / 2 : LARA_MAX_HEALTH)); 3273 game->playSound(TR::SND_HEALTH, pos, Sound::PAN); 3274 inventory->remove(usedItem); 3275 } 3276 usedItem = TR::Entity::NONE; 3277 default : ; 3278 } 3279 3280 Character::update(); 3281 for (int i = 0; i < COUNT(braid); i++) { 3282 if (braid[i]) { 3283 braid[i]->update(); 3284 } 3285 } 3286 } 3287 3288 camera->update(); 3289 3290 if (hitTimer > 0.0f) { 3291 hitTimer -= Core::deltaTime; 3292 if (hitTimer > 0.0f) 3293 Input::setJoyVibration(camera->cameraIndex, 0.5f, 0.5f); 3294 else 3295 Input::setJoyVibration(camera->cameraIndex, 0, 0); 3296 } 3297 3298 if (level->isCutsceneLevel()) 3299 return; 3300 3301 if (damageTime > 0.0f) 3302 damageTime = max(0.0f, damageTime - Core::deltaTime); 3303 3304 if (stand == STAND_UNDERWATER && !dozy) { 3305 if (oxygen > 0.0f) 3306 oxygen -= Core::deltaTime; 3307 else 3308 hit(Core::deltaTime * 150.0f); 3309 } else 3310 if (oxygen < LARA_MAX_OXYGEN && health > 0.0f) 3311 oxygen = min(LARA_MAX_OXYGEN, oxygen + Core::deltaTime * 10.0f); 3312 3313 usedItem = TR::Entity::NONE; 3314 3315 if (camera->mode != Camera::MODE_CUTSCENE && camera->mode != Camera::MODE_STATIC) { 3316 camera->mode = (emptyHands() || health <= 0.0f) ? Camera::MODE_FOLLOW : Camera::MODE_COMBAT; 3317 if (input & LOOK) 3318 camera->mode = Camera::MODE_LOOK; 3319 } 3320 3321 if (keyItem) { 3322 keyItem->flags.invisible = animation.frameIndex < (state == STATE_USE_KEY ? 70 : 30); 3323 keyItem->lockMatrix = true; 3324 mat4 &m = keyItem->matrix; 3325 Basis b; 3326 3327 if (state == STATE_USE_KEY) { 3328 b = getJoint(10); 3329 b.rot = b.rot * quat(vec3(0, 1, 0), PI * 0.5f); 3330 b.translate(vec3(0, 120, 0)); 3331 } else { 3332 vec3 rot(0.0f); 3333 3334 // TODO: hardcode item-hand alignment 8) 3335 rot.x = -PI * 0.5f; 3336 3337 if (animation.frameIndex < 55) 3338 b = getJoint(13); 3339 else 3340 b = getJoint(10); 3341 3342 b.translate(vec3(0, 48, 0)); 3343 3344 if (rot.x != 0.0f) b.rot = b.rot * quat(vec3(1, 0, 0), rot.x); 3345 if (rot.y != 0.0f) b.rot = b.rot * quat(vec3(0, 1, 0), rot.y); 3346 if (rot.z != 0.0f) b.rot = b.rot * quat(vec3(0, 0, 1), rot.z); 3347 } 3348 3349 keyItem->joints[0] = b; 3350 3351 m.identity(); 3352 m.setRot(b.rot); 3353 m.setPos(b.pos); 3354 //m = getMatrix(); 3355 } 3356 } 3357 updateAnimationLara3358 virtual void updateAnimation(bool commands) { 3359 Controller::updateAnimation(commands); 3360 updateWeapon(); 3361 3362 if (stand == STAND_UNDERWATER) 3363 specular = LARA_MIN_SPECULAR; 3364 else 3365 if (specular > LARA_MIN_SPECULAR) 3366 specular = max(LARA_MIN_SPECULAR, specular - LARA_WET_TIMER * Core::deltaTime); 3367 3368 if (state == STATE_MIDAS_DEATH || state == STATE_MIDAS_USE) { 3369 uint32 sparklesMask = getMidasMask(); 3370 3371 if (state == STATE_MIDAS_DEATH) 3372 visibleMask = sparklesMask ^ 0xFFFFFFFF; 3373 3374 timer += Core::deltaTime; 3375 if (timer >= 1.0f / 30.0f) { 3376 timer -= 1.0f / 30.0f; 3377 addSparks(sparklesMask); 3378 } 3379 } 3380 } 3381 updateVelocityLara3382 virtual void updateVelocity() { 3383 flowVelocity = vec3(0); 3384 3385 if (!(input & DEATH) && !level->isCutsceneLevel()) 3386 checkTrigger(this, false); 3387 3388 // get turning angle 3389 float w = ((input & WALK) && state != STATE_WALK) ? 0.0f : ((input & LEFT) ? -1.0f : ((input & RIGHT) ? 1.0f : 0.0f)); 3390 3391 if (state == STATE_SWIM || state == STATE_GLIDE) 3392 w *= TURN_WATER_FAST; 3393 else if (state == STATE_TREAD || state == STATE_SURF_TREAD || state == STATE_SURF_SWIM || state == STATE_SURF_BACK) 3394 w *= TURN_WATER_FAST; 3395 else if (state == STATE_RUN) { 3396 if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) 3397 w *= TURN_FAST; 3398 else 3399 w *= sign(w) != sign(tilt) ? 0.0f : w * TURN_FAST * tilt / LARA_TILT_MAX; 3400 } else if (state == STATE_FAST_TURN) 3401 w *= TURN_FAST; 3402 else if (state == STATE_FAST_BACK) 3403 w *= TURN_FAST_BACK; 3404 else if (state == STATE_TURN_LEFT || state == STATE_TURN_RIGHT || state == STATE_WALK || (state == STATE_STOP && animation.index == ANIM_LANDING)) 3405 w *= TURN_NORMAL; 3406 else if (state == STATE_FORWARD_JUMP || state == STATE_BACK || state == STATE_WADE) 3407 w *= TURN_SLOW; 3408 else 3409 w = 0.0f; 3410 3411 if (w != 0.0f) 3412 rotateY(w * rotFactor.y * Core::deltaTime); 3413 // pitch (underwater only) 3414 if (stand == STAND_UNDERWATER && (((input & FORTH) != 0) ^ ((input & BACK) != 0))) 3415 rotateX(((input & FORTH) ? -TURN_WATER_SLOW : TURN_WATER_SLOW) * rotFactor.x * Core::deltaTime); 3416 3417 // get animation direction 3418 angleExt = angle.y; 3419 switch (state) { 3420 case STATE_BACK : 3421 case STATE_SURF_BACK : 3422 case STATE_BACK_JUMP : 3423 case STATE_FAST_BACK : 3424 case STATE_SLIDE_BACK : 3425 case STATE_ROLL_END : 3426 angleExt += PI; 3427 break; 3428 case STATE_LEFT_JUMP : 3429 case STATE_STEP_LEFT : 3430 case STATE_SURF_LEFT : 3431 case STATE_HANG_LEFT : 3432 angleExt -= PI * 0.5f; 3433 break; 3434 case STATE_RIGHT_JUMP : 3435 case STATE_STEP_RIGHT : 3436 case STATE_SURF_RIGHT : 3437 case STATE_HANG_RIGHT : 3438 angleExt += PI * 0.5f; 3439 break; 3440 } 3441 3442 switch (stand) { 3443 case STAND_AIR : 3444 applyGravity(velocity.y); 3445 if (velocity.y >= 154.0f && state == STATE_FALL) 3446 game->playSound(TR::SND_SCREAM, pos, Sound::PAN | Sound::UNIQUE); 3447 /* 3448 if (state == STATE_FALL || state == STATE_FAST_DIVE) { 3449 velocity.x *= 0.95 * Core::deltaTime; 3450 velocity.z *= 0.95 * Core::deltaTime; 3451 } 3452 */ 3453 break; 3454 case STAND_GROUND : 3455 case STAND_WADE : 3456 case STAND_SLIDE : 3457 case STAND_HANG : 3458 case STAND_ONWATER : { 3459 3460 switch (state) { 3461 case STATE_SURF_SWIM : 3462 case STATE_SURF_BACK : 3463 case STATE_SURF_LEFT : 3464 case STATE_SURF_RIGHT : 3465 speed = min(speed + 30.0f * LARA_WATER_ACCEL * Core::deltaTime, LARA_SURF_SPEED); 3466 break; 3467 default : 3468 speed = animation.getSpeed(); 3469 } 3470 3471 if (stand == STAND_ONWATER) { 3472 velocity.x = sinf(angleExt) * speed; 3473 velocity.z = cosf(angleExt) * speed; 3474 velocity.y = 0.0f; 3475 } else { 3476 TR::Level::FloorInfo info; 3477 if (stand == STAND_HANG) { 3478 vec3 p = pos + getDir() * (LARA_RADIUS + 2.0f); 3479 getFloorInfo(getRoomIndex(), p, info); 3480 if (info.roomAbove != TR::NO_ROOM && info.floor >= pos.y - LARA_HANG_OFFSET) 3481 getFloorInfo(info.roomAbove, p, info); 3482 } else 3483 getFloorInfo(getRoomIndex(), pos, info); 3484 3485 vec3 v(sinf(angleExt), 0.0f, cosf(angleExt)); 3486 velocity = info.getSlant(v) * speed; 3487 } 3488 break; 3489 } 3490 case STAND_UNDERWATER : { 3491 if (animation.index == ANIM_TO_UNDERWATER) 3492 speed = 15.0f; 3493 if (state == STATE_SWIM) 3494 speed = min(speed + 30.0f * LARA_WATER_ACCEL * Core::deltaTime, LARA_SWIM_SPEED); 3495 if (state == STATE_TREAD || state == STATE_GLIDE) 3496 speed = max(speed - 30.0f * LARA_SWIM_FRICTION * Core::deltaTime, 0.0f); 3497 velocity = vec3(angle.x, angle.y) * speed; 3498 // TODO: apply flow velocity 3499 break; 3500 } 3501 } 3502 3503 if (state == STATE_DEATH || state == STATE_UNDERWATER_DEATH) 3504 velocity.x = velocity.z = 0.0f; 3505 } 3506 updatePositionLara3507 virtual void updatePosition() { // TODO: sphere / bbox collision 3508 if (level->isCutsceneLevel()) 3509 return; 3510 3511 // tilt control 3512 vec2 vTilt(LARA_TILT_SPEED * Core::deltaTime, LARA_TILT_MAX); 3513 if (stand == STAND_UNDERWATER) 3514 vTilt *= 2.0f; 3515 vTilt *= rotFactor.y; 3516 bool VR = (Core::settings.detail.stereo == Core::Settings::STEREO_VR) && camera->firstPerson; 3517 updateTilt((input & WALK) == 0 && (state == STATE_RUN || (state == STATE_STOP && animation.index == ANIM_LANDING) || stand == STAND_UNDERWATER) && !VR, vTilt.x, vTilt.y); 3518 3519 collisionOffset = vec3(0.0f); 3520 3521 if (checkCollisions() || (velocity + flowVelocity + collisionOffset).length2() >= 1.0f) { // TODO: stop & smash anim 3522 vec3 oldPos = pos; 3523 3524 move(); 3525 3526 statsDistDelta += (pos - oldPos).length(); 3527 while (statsDistDelta >= UNITS_PER_METER) { 3528 statsDistDelta -= UNITS_PER_METER; 3529 saveStats.distance++; 3530 } 3531 } 3532 } 3533 getPosLara3534 virtual vec3 getPos() { 3535 return level->isCutsceneLevel() ? getViewPoint() : pos; 3536 } 3537 checkCollisionsLara3538 bool checkCollisions() { 3539 // check static objects (TODO: check linked rooms?) 3540 const TR::Room &room = getRoom(); 3541 Box box(pos - vec3(LARA_RADIUS, LARA_HEIGHT, LARA_RADIUS), pos + vec3(LARA_RADIUS, 0.0f, LARA_RADIUS)); 3542 3543 for (int i = 0; i < room.meshesCount; i++) { 3544 TR::Room::Mesh &m = room.meshes[i]; 3545 TR::StaticMesh &sm = level->staticMeshes[m.meshIndex]; 3546 if (sm.flags != 2) continue; 3547 Box meshBox; 3548 sm.getBox(true, m.rotation, meshBox); 3549 meshBox.translate(vec3(float(m.x), float(m.y), float(m.z))); 3550 if (!box.intersect(meshBox)) continue; 3551 3552 collisionOffset += meshBox.pushOut2D(box); 3553 } 3554 3555 // check enemies & doors 3556 for (int i = 0; i < level->entitiesCount; i++) { 3557 const TR::Entity &e = level->entities[i]; 3558 3559 Controller *controller = (Controller*)e.controller; 3560 3561 if (!controller || controller->flags.invisible || !controller->isCollider()) continue; 3562 3563 if (e.type == TR::Entity::TRAP_DOOR_1 || e.type == TR::Entity::TRAP_DOOR_2) continue; 3564 3565 if (e.isEnemy()) { 3566 if (e.type != TR::Entity::ENEMY_REX && (controller->flags.active != TR::ACTIVE || ((Character*)controller)->health <= 0)) continue; 3567 } else { 3568 // fast distance check for object 3569 if (e.type != TR::Entity::HAMMER_HANDLE && e.type != TR::Entity::HAMMER_BLOCK && e.type != TR::Entity::SCION_HOLDER) 3570 if (fabsf(pos.x - controller->pos.x) > COLLIDE_MAX_RANGE || 3571 fabsf(pos.z - controller->pos.z) > COLLIDE_MAX_RANGE || 3572 fabsf(pos.y - controller->pos.y) > COLLIDE_MAX_RANGE) continue; 3573 } 3574 3575 if (e.type == TR::Entity::TRAP_BOULDER && !controller->flags.unused) continue; // boulder should stay still 3576 3577 vec3 dir = pos - vec3(0.0f, 128.0f, 0.0f) - controller->pos; 3578 vec3 p = dir.rotateY(controller->angle.y); 3579 3580 Box box = controller->getBoundingBoxLocal(); 3581 box.expand(vec3(LARA_RADIUS + 50.0f, 0.0f, LARA_RADIUS + 50.0f)); 3582 box.max.y += LARA_HEIGHT; 3583 3584 if (!box.contains(p)) // TODO: Box vs Box or check Lara's head point? (check thor hammer handle) 3585 continue; 3586 3587 if (!collide(controller, false)) 3588 continue; 3589 3590 if (e.isEnemy()) { // enemy collision 3591 // velocity.x = velocity.y = 0.0f; 3592 } else { 3593 p += box.pushOut2D(p); 3594 p = (p.rotateY(-controller->angle.y) + controller->pos) - pos; 3595 collisionOffset += vec3(p.x, 0.0f, p.z); 3596 } 3597 3598 if (e.type == TR::Entity::ENEMY_REX && ((Character*)controller)->health <= 0) 3599 return true; 3600 if (!e.isEnemy() || e.type == TR::Entity::ENEMY_BAT) 3601 return true; 3602 3603 if (canHitAnim()) { // TODO: check enemy type and health here 3604 // get hit dir 3605 if (hitDir == -1) { 3606 if (health > 0) 3607 game->playSound(TR::SND_HIT, pos, Sound::PAN); 3608 hitTime = 0.0f; 3609 } 3610 3611 if (level->version & TR::VER_TR1) // TODO: check hit animation indices for TR2 and TR3 3612 hitDir = angleQuadrant(dir.rotateY(angle.y + PI * 0.5f).angleY(), 0.25f); 3613 return true; 3614 } 3615 }; 3616 3617 if (lightning && lightning->flash && !lightning->armed) { 3618 if (hitDir == -1) 3619 hitTime = 0.0f; 3620 hitDir = rand() % 4; 3621 } else { 3622 hitDir = -1; 3623 lightning = NULL; 3624 } 3625 return false; 3626 } 3627 3628 #ifdef _OS_3DS // for some reason move() math works incorrect on 3DS 3629 #pragma GCC push_options 3630 #pragma GCC optimize ("O0") 3631 #endif 3632 moveLara3633 void move() { 3634 vec3 vel = (velocity + flowVelocity) * Core::deltaTime * 30.0f + collisionOffset; 3635 vec3 opos(pos), offset(0.0f); 3636 3637 float radius = stand == STAND_UNDERWATER ? LARA_RADIUS_WATER : LARA_RADIUS; 3638 int maxHeight = stand == STAND_UNDERWATER ? LARA_HEIGHT_WATER : LARA_HEIGHT; 3639 int minHeight = 0; 3640 int maxAscent = 256 + 128; 3641 int maxDescent = 0xFFFFFF; 3642 3643 int room = getRoomIndex(); 3644 3645 if (state == STATE_WALK || state == STATE_BACK) 3646 maxDescent = maxAscent; 3647 if (state == STATE_STEP_LEFT || state == STATE_STEP_RIGHT) 3648 maxAscent = maxDescent = 64; 3649 if (stand == STAND_ONWATER) { 3650 maxAscent = -2; 3651 maxHeight = 0; 3652 offset.y = -1; 3653 } 3654 3655 bool standHang = stand == STAND_HANG && state != STATE_HANG_UP && state != STATE_HANDSTAND; 3656 3657 if (standHang) { 3658 maxHeight = 0; 3659 maxAscent = maxDescent = 64; 3660 offset = getDir() * (LARA_RADIUS + 32.0f); 3661 offset.y -= LARA_HANG_OFFSET + 32; 3662 } 3663 if (stand == STAND_UNDERWATER) { 3664 offset.y += LARA_HEIGHT_WATER * 0.5f; 3665 } 3666 3667 collision = Collision(this, room, pos, offset, vel, radius, angleExt, minHeight, maxHeight, maxAscent, maxDescent); 3668 3669 if (!standHang && (collision.side == Collision::LEFT || collision.side == Collision::RIGHT)) { 3670 float rot = TURN_WALL_Y * Core::deltaTime; 3671 rotateY((collision.side == Collision::LEFT) ? rot : -rot); 3672 } 3673 3674 if (standHang && collision.side != Collision::FRONT) { 3675 offset.x = offset.z = 0.0f; 3676 minHeight = LARA_HANG_OFFSET; 3677 maxDescent = 0xFFFFFF; 3678 maxAscent = -LARA_HANG_OFFSET; 3679 vec3 p = pos; 3680 collision = Collision(this, room, p, offset, vec3(0.0f), radius, angleExt, minHeight, maxHeight, maxAscent, maxDescent); 3681 if (collision.side == Collision::FRONT) 3682 pos = opos; 3683 } 3684 3685 bool isLeftFoot = getLeadingFoot(); 3686 3687 if (stand == STAND_UNDERWATER) { 3688 if (collision.side == Collision::TOP) 3689 rotateX(-TURN_WALL_X * Core::deltaTime); 3690 if (collision.side == Collision::BOTTOM) 3691 rotateX( TURN_WALL_X * Core::deltaTime); 3692 } 3693 3694 if (stand == STAND_AIR && collision.side == Collision::TOP && velocity.y < 0.0f) 3695 velocity.y = 30.0f; 3696 3697 if (collision.side == Collision::FRONT) { 3698 float floor = collision.info[Collision::FRONT].floor; 3699 /* 3700 switch (angleQuadrant(angleExt - angle.y), 0.25f) { 3701 case 0 : collision.side = Collision::FRONT; LOG("FRONT\n"); break; 3702 case 1 : collision.side = Collision::RIGHT; LOG("RIGHT\n"); break; 3703 case 2 : collision.side = Collision::BACK; LOG("BACK\n"); break; 3704 case 3 : collision.side = Collision::LEFT; LOG("LEFT\n"); break; 3705 } 3706 */ 3707 if (velocity.dot(getDir()) <= EPS) 3708 collision.side = Collision::NONE; 3709 3710 // hit the wall 3711 switch (stand) { 3712 case STAND_AIR : 3713 if (state == STATE_UP_JUMP || state == STATE_REACH || state == STATE_FALL_BACK) 3714 velocity.x = velocity.z = 0.0f; 3715 3716 if (velocity.x != 0.0f || velocity.z != 0.0f) { 3717 animation.setAnim(ANIM_SMASH_JUMP); 3718 velocity.x = -velocity.x * 0.25f; 3719 velocity.z = -velocity.z * 0.25f; 3720 if (velocity.y < 0.0f) 3721 velocity.y = 30.0f; 3722 } 3723 break; 3724 case STAND_GROUND : 3725 case STAND_HANG : 3726 case STAND_WADE : 3727 if (opos.y - floor > (256 * 3 - 128) && state == STATE_RUN) { 3728 if (input & ACTION) { 3729 animation.setAnim(isLeftFoot ? ANIM_STAND_LEFT : ANIM_STAND_RIGHT); 3730 } else { 3731 animation.setAnim(isLeftFoot ? ANIM_SMASH_RUN_LEFT : ANIM_SMASH_RUN_RIGHT); 3732 } 3733 } else if (stand == STAND_HANG) 3734 animation.setAnim(ANIM_HANG, -21); 3735 else if (state != STATE_ROLL_START && state != STATE_ROLL_END) 3736 animation.setAnim((state == STATE_RUN || state == STATE_WALK) ? (isLeftFoot ? ANIM_STAND_LEFT : ANIM_STAND_RIGHT) : ANIM_STAND); 3737 velocity.x = velocity.z = 0.0f; 3738 break; 3739 case STAND_UNDERWATER : 3740 if (fabsf(angle.x) > TURN_WALL_X_CLAMP) 3741 rotateX(TURN_WALL_X * Core::deltaTime * sign(angle.x)); 3742 else 3743 pos.y = opos.y; 3744 break; 3745 default : ;// no smash animation 3746 } 3747 } else { 3748 if (stand == STAND_GROUND) { 3749 float floor = collision.info[Collision::NONE].floor; 3750 float h = floor - opos.y; 3751 3752 if (h >= 128 && (state == STATE_WALK || state == STATE_BACK)) { // descend 3753 if (state == STATE_WALK) animation.setAnim(isLeftFoot ? ANIM_WALK_DESCEND_LEFT : ANIM_WALK_DESCEND_RIGHT); 3754 if (state == STATE_BACK) animation.setAnim(isLeftFoot ? ANIM_BACK_DESCEND_LEFT : ANIM_BACK_DESCEND_RIGHT); 3755 pos.y = float(floor); 3756 } else if (h > -1.0f) { 3757 pos.y = min(float(floor), pos.y + DESCENT_SPEED * Core::deltaTime); 3758 } else if (h > -128) { 3759 pos.y = float(floor); 3760 } else if (h >= -(256 + 128) && (state == STATE_RUN || state == STATE_WALK)) { // ascend 3761 if (state == STATE_RUN) animation.setAnim(isLeftFoot ? ANIM_RUN_ASCEND_LEFT : ANIM_RUN_ASCEND_RIGHT); 3762 if (state == STATE_WALK) animation.setAnim(isLeftFoot ? ANIM_WALK_ASCEND_LEFT : ANIM_WALK_ASCEND_RIGHT); 3763 pos.y = float(floor); 3764 } else 3765 pos.y = float(floor); 3766 } 3767 collision.side = Collision::NONE; 3768 } 3769 3770 if (dozy) stand = STAND_GROUND; 3771 updateRoom(); 3772 if (stand == STAND_UNDERWATER && !(waterLevel == 0.0f && waterDepth == 0.0f) && pos.y <= waterLevel) { 3773 if (waterLevel - pos.y < 256.0f) { 3774 pos.y = waterLevel; 3775 updateRoom(); 3776 stand = STAND_ONWATER; 3777 } else 3778 stand = STAND_AIR; 3779 } 3780 if (dozy) stand = STAND_UNDERWATER; 3781 } 3782 3783 #ifdef _OS_3DS 3784 #pragma GCC pop_options 3785 #endif 3786 applyFlowLara3787 virtual void applyFlow(TR::Camera &sink) { 3788 if (stand != STAND_UNDERWATER && stand != STAND_ONWATER) return; 3789 3790 vec3 target = vec3(float(sink.x), float(sink.y), float(sink.z)); 3791 3792 #ifdef _DEBUG 3793 //delete[] dbgBoxes; 3794 //dbgBoxes = NULL; 3795 #endif 3796 3797 if (box != sink.flags.boxIndex) { 3798 uint16 *boxes; 3799 uint16 count = game->findPath(0xFFFFFF, -0xFFFFFF, false, box, sink.flags.boxIndex, getZones(), &boxes); 3800 if (count > 1) { 3801 #ifdef _DEBUG 3802 //dbgBoxesCount = count; 3803 //dbgBoxes = new uint16[dbgBoxesCount]; 3804 //memcpy(dbgBoxes, boxes, sizeof(uint16) * dbgBoxesCount); 3805 #endif 3806 TR::Box &b = level->boxes[boxes[1]]; 3807 target.x = (b.minX + b.maxX) * 0.5f; 3808 if (target.y > b.floor) 3809 target.y = float(b.floor); 3810 target.z = (b.minZ + b.maxZ) * 0.5f; 3811 } 3812 } 3813 3814 flowVelocity = vec3(0); 3815 if (!dozy) { 3816 float speed = sink.speed * 6.0f; 3817 flowVelocity.x = clamp(target.x - pos.x, -speed, +speed); 3818 flowVelocity.y = clamp(target.y - pos.y, -speed, +speed); 3819 flowVelocity.z = clamp(target.z - pos.z, -speed, +speed); 3820 3821 if (stand == STAND_ONWATER) 3822 goUnderwater(); 3823 } 3824 } 3825 getMidasMaskLara3826 uint32 getMidasMask() { 3827 if (state == STATE_MIDAS_USE) 3828 return JOINT_MASK_ARM_L3 | JOINT_MASK_ARM_R3; 3829 3830 uint32 mask = 0; 3831 int frame = animation.frameIndex; 3832 if (frame > 4 ) mask |= JOINT_MASK_LEG_L3 | JOINT_MASK_LEG_R3; 3833 if (frame > 69 ) mask |= JOINT_MASK_LEG_L2; 3834 if (frame > 79 ) mask |= JOINT_MASK_LEG_L1; 3835 if (frame > 99 ) mask |= JOINT_MASK_LEG_R2; 3836 if (frame > 119) mask |= JOINT_MASK_LEG_R1 | JOINT_MASK_HIPS; 3837 if (frame > 134) mask |= JOINT_MASK_CHEST; 3838 if (frame > 149) mask |= JOINT_MASK_ARM_L1; 3839 if (frame > 162) mask |= JOINT_MASK_ARM_L2; 3840 if (frame > 173) mask |= JOINT_MASK_ARM_L3; 3841 if (frame > 185) mask |= JOINT_MASK_ARM_R1; 3842 if (frame > 194) mask |= JOINT_MASK_ARM_R2; 3843 if (frame > 217) mask |= JOINT_MASK_ARM_R3; 3844 if (frame > 224) mask |= JOINT_MASK_HEAD; 3845 return mask; 3846 } 3847 renderLara3848 virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { 3849 uint32 visMask = visibleMask; 3850 if (Core::pass != Core::passShadow && camera->firstPerson && camera->viewIndex == -1 && game->getCamera() == camera) // hide head in first person view // TODO: fix for firstPerson with viewIndex always == -1 3851 visibleMask &= ~JOINT_MASK_HEAD; 3852 Controller::render(frustum, mesh, type, caustics); 3853 3854 if (level->extra.laraJoints > -1) { 3855 const TR::Model *model = getModel(); 3856 for (int i = 0; i < model->mCount; i++) { 3857 joints[i].w = 1.0f; 3858 } 3859 Core::setBasis(joints, model->mCount); 3860 mesh->renderModel(level->extra.laraJoints, caustics); 3861 } 3862 3863 visibleMask = visMask; 3864 3865 for (int i = 0; i < COUNT(braid); i++) { 3866 if (braid[i]) { 3867 braid[i]->render(mesh); 3868 } 3869 } 3870 3871 if (state == STATE_MIDAS_DEATH /* && Core::pass == Core::passCompose */) { 3872 game->setRoomParams(getRoomIndex(), Shader::MIRROR, 1.2f, 1.0f, 0.2f, 1.0f, false); 3873 /* catsuit test 3874 game->setRoomParams(getRoomIndex(), Shader::MIRROR, 0.3f, 0.3f, 0.3f, 1.0f, false); 3875 Core::updateLights(); 3876 */ 3877 GAPI::Texture *dtex = Core::active.textures[sDiffuse]; 3878 3879 environment->bind(sDiffuse); 3880 visibleMask ^= 0xFFFFFFFF; 3881 Controller::render(frustum, mesh, type, caustics); 3882 visibleMask ^= 0xFFFFFFFF; 3883 3884 if (dtex) dtex->bind(sDiffuse); 3885 } 3886 } 3887 }; 3888 3889 #endif 3890