1 #ifndef H_CONTROLLER 2 #define H_CONTROLLER 3 4 #include "format.h" 5 #include "frustum.h" 6 #include "mesh.h" 7 #include "animation.h" 8 9 #define GRAVITY 6.0f 10 #define SPRITE_FPS 10.0f 11 12 #define MAX_LAYERS 4 13 14 #define UNLIMITED_AMMO 10000 15 16 struct Controller; 17 18 struct ICamera { 19 enum Mode { 20 MODE_FOLLOW, 21 MODE_STATIC, 22 MODE_LOOK, 23 MODE_COMBAT, 24 MODE_CUTSCENE, 25 MODE_HEAVY, 26 } mode; 27 28 int cameraIndex; 29 vec4 *reflectPlane; 30 vec3 angle; 31 float shake; 32 bool firstPerson; 33 bool centerView; 34 TR::Location eye, target; 35 ICameraICamera36 ICamera() : cameraIndex(0), reflectPlane(NULL), angle(0.0f), shake(0.0f), centerView(false) {} 37 setupICamera38 virtual void setup(bool calcMatrices) {} getRoomIndexICamera39 virtual int getRoomIndex() const { return TR::NO_ROOM; } doCutsceneICamera40 virtual void doCutscene(const vec3 &pos, float rotation) {} getOwnerICamera41 virtual Controller* getOwner() { return NULL; } 42 setAngleICamera43 void setAngle(float x, float y) { 44 angle.x = x * DEG2RAD; 45 angle.y = y * DEG2RAD; 46 } 47 }; 48 49 struct RoomDesc { 50 int32 index; 51 vec4 portal; 52 RoomDescRoomDesc53 RoomDesc() {} RoomDescRoomDesc54 RoomDesc(int32 index, const vec4 &portal) : index(index), portal(portal) {} 55 }; 56 57 struct IGame { ~IGameIGame58 virtual ~IGame() {} loadLevelIGame59 virtual void loadLevel(TR::LevelID id) {} loadNextLevelIGame60 virtual void loadNextLevel() {} saveGameIGame61 virtual void saveGame(TR::LevelID id, bool checkpoint, bool updateStats) {} loadGameIGame62 virtual void loadGame(int slot) {} applySettingsIGame63 virtual void applySettings(const Core::Settings &settings) {} 64 getLevelIGame65 virtual TR::Level* getLevel() { return NULL; } getMeshIGame66 virtual MeshBuilder* getMesh() { return NULL; } 67 virtual ICamera* getCamera(int index = -1) { return NULL; } 68 virtual Controller* getLara(int index = 0) { return NULL; } getLaraIGame69 virtual Controller* getLara(const vec3 &pos) { return NULL; } isCutsceneIGame70 virtual bool isCutscene() { return false; } getRandomBoxIGame71 virtual uint16 getRandomBox(uint16 zone, uint16 *zones) { return 0; } findPathIGame72 virtual uint16 findPath(int ascend, int descend, bool big, int boxStart, int boxEnd, uint16 *zones, uint16 **boxes) { return 0; } 73 virtual void flipMap(bool water = true) {} setWaterParamsIGame74 virtual void setWaterParams(float height) {} waterDropIGame75 virtual void waterDrop(const vec3 &pos, float radius, float strength) {} 76 virtual void setShader(Core::Pass pass, Shader::Type type, bool underwater = false, bool alphaTest = false) {} 77 virtual void setRoomParams(int roomIndex, Shader::Type type, float diffuse, float ambient, float specular, float alpha, bool alphaTest = false) {} setupBindingIGame78 virtual void setupBinding() {} 79 virtual void getVisibleRooms(RoomDesc *roomsList, int &roomsCount, int from, int to, const vec4 &viewPort, bool water, int count = 0) {} 80 virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0, Core::Pass pass = Core::passAmbient) {} renderModelFullIGame81 virtual void renderModelFull(int modelIndex, bool underwater, Basis *joints) {} renderComposeIGame82 virtual void renderCompose(int roomIndex) {} 83 virtual void renderView(int roomIndex, bool water, bool showUI, int roomsCount = 0, RoomDesc *roomsList = NULL) {} renderGameIGame84 virtual void renderGame(bool showUI, bool invBG) {} setEffectIGame85 virtual void setEffect(Controller *controller, TR::Effect::Type effect) {} 86 projectPointIGame87 virtual vec4 projectPoint(const vec4 &p) { return vec4(0.0f); } checkTriggerIGame88 virtual void checkTrigger(Controller *controller, bool heavy) {} 89 virtual void shakeCamera(float value, bool add = false) {} 90 91 virtual Controller* addEntity(TR::Entity::Type type, int room, const vec3 &pos, float angle = 0.0f) { return NULL; } removeEntityIGame92 virtual void removeEntity(Controller *controller) {} 93 addMuzzleFlashIGame94 virtual void addMuzzleFlash(Controller *owner, int joint, const vec3 &offset, int lightIndex) {} 95 96 virtual void invShow(int playerIndex, int page, int itemIndex = -1) {} invUseIGame97 virtual bool invUse(int playerIndex, TR::Entity::Type type) { return false; } 98 virtual void invAdd(TR::Entity::Type type, int count = 1) {} invCountIGame99 virtual int* invCount(TR::Entity::Type type) { return NULL; } invChooseKeyIGame100 virtual bool invChooseKey(int playerIndex, TR::Entity::Type hole) { return false; } 101 102 virtual Sound::Sample* playSound(int id, const vec3 &pos = vec3(0.0f), int flags = 0) const { return NULL; } 103 virtual void playTrack(uint8 track, bool background = false) {} stopTrackIGame104 virtual void stopTrack() {} 105 }; 106 107 struct Controller { 108 109 static Controller *first; 110 Controller *next; 111 112 IGame *game; 113 TR::Level *level; 114 int entity; 115 116 Animation animation; 117 int &state; 118 119 vec3 pos; 120 vec3 angle; 121 122 int16 roomIndex; 123 TR::Entity::Flags flags; 124 125 Basis *joints; 126 int jointsFrame; 127 128 vec4 ambient[6]; 129 float specular; 130 float intensity; 131 132 float timer; 133 134 TR::Room::Light *targetLight; 135 vec3 mainLightPos; 136 vec4 mainLightColor; 137 bool mainLightFlip; 138 bool invertAim; 139 bool lockMatrix; 140 141 struct MeshLayer { 142 uint32 model; 143 uint32 mask; 144 } *layers; 145 146 uint32 visibleMask; 147 uint32 explodeMask; 148 struct ExplodePart { 149 Basis basis; 150 vec3 velocity; 151 float damage; 152 int16 roomIndex; 153 } *explodeParts; 154 155 vec3 lastPos; 156 mat4 matrix; 157 158 float waterLevel, waterDepth; 159 ControllerController160 Controller(IGame *game, int entity) : next(NULL), game(game), level(game->getLevel()), entity(entity), animation(level, getModel(), level->entities[entity].flags.smooth), state(animation.state), invertAim(false), layers(0), explodeMask(0), explodeParts(0), lastPos(0) { 161 const TR::Entity &e = getEntity(); 162 lockMatrix = false; 163 matrix.identity(); 164 165 waterLevel = waterDepth = 0.0f; 166 167 pos = vec3(float(e.x), float(e.y), float(e.z)); 168 angle = vec3(0.0f, e.rotation, 0.0f); 169 roomIndex = e.room; 170 flags = e.flags; 171 flags.state = TR::Entity::asNone; 172 173 const TR::Model *m = getModel(); 174 joints = m ? new Basis[m->mCount] : NULL; 175 jointsFrame = -1; 176 177 specular = 0.0f; 178 intensity = e.intensity == -1 ? -1.0f : intensityf(e.intensity); 179 timer = 0.0f; 180 ambient[0] = ambient[1] = ambient[2] = ambient[3] = ambient[4] = ambient[5] = vec4(intensityf(getRoom().ambient)); 181 targetLight = NULL; 182 mainLightFlip = false; 183 updateLights(false); 184 visibleMask = 0xFFFFFFFF; 185 186 if (flags.once) { 187 flags.invisible = true; 188 flags.once = false; 189 } 190 191 if (flags.active == TR::ACTIVE) { 192 flags.active = 0; 193 flags.reverse = true; 194 activate(); 195 } 196 197 level->entities[entity].flags = flags; 198 199 if (e.isLara() || e.isActor()) // Lara and cutscene entities is active by default 200 activate(); 201 } 202 ~ControllerController203 virtual ~Controller() { 204 delete[] joints; 205 delete[] layers; 206 delete[] explodeParts; 207 deactivate(true); 208 } 209 updateModelController210 void updateModel() { 211 const TR::Model *model = getModel(); 212 213 if (!model || model == animation.model) 214 return; 215 animation.setModel(model); 216 delete[] joints; 217 joints = new Basis[model->mCount]; 218 } 219 fixRoomIndexController220 bool fixRoomIndex() { // TODO: remove this and fix braid 221 vec3 p = getPos(); 222 if (insideRoom(p, roomIndex)) 223 return true; 224 for (int i = 0; i < level->roomsCount; i++) 225 if (insideRoom(p, i)) { 226 roomIndex = i; 227 return true; 228 } 229 return false; 230 } 231 getFloorInfoController232 void getFloorInfo(int roomIndex, const vec3 &pos, TR::Level::FloorInfo &info) const { 233 int x = int(pos.x); 234 int y = int(pos.y); 235 int z = int(pos.z); 236 237 int dx, dz; 238 TR::Room::Sector &s = level->getSector(roomIndex, x, z, dx, dz); 239 240 info.roomFloor = float(256 * s.floor); 241 info.roomCeiling = float(256 * s.ceiling); 242 info.floor = info.roomFloor; 243 info.ceiling = info.roomCeiling; 244 info.slantX = 0; 245 info.slantZ = 0; 246 info.roomNext = TR::NO_ROOM; 247 info.roomBelow = s.roomBelow; 248 info.roomAbove = s.roomAbove; 249 info.floorIndex = s.floorIndex; 250 info.boxIndex = s.boxIndex; 251 info.lava = false; 252 info.climb = 0; 253 info.trigger = TR::Level::Trigger::ACTIVATE; 254 info.trigCmdCount = 0; 255 256 //if (s.floor == TR::NO_FLOOR) 257 // return; 258 259 TR::Room::Sector *sBelow = &s; 260 while (sBelow->roomBelow != TR::NO_ROOM) sBelow = &level->getSector(sBelow->roomBelow, x, z, dx, dz); 261 info.floor = float(256 * sBelow->floor); 262 263 parseFloorData(info, sBelow->floorIndex, dx, dz); 264 265 if (info.roomNext == TR::NO_ROOM) { 266 TR::Room::Sector *sAbove = &s; 267 while (sAbove->roomAbove != TR::NO_ROOM) sAbove = &level->getSector(sAbove->roomAbove, x, z, dx, dz); 268 if (sAbove != sBelow) { 269 TR::Level::FloorInfo tmpInfo; 270 tmpInfo.ceiling = float(256 * sAbove->ceiling); 271 parseFloorData(tmpInfo, sAbove->floorIndex, dx, dz); 272 info.ceiling = tmpInfo.ceiling; 273 } 274 } else { 275 int tmp = info.roomNext; 276 getFloorInfo(tmp, pos, info); 277 info.roomNext = tmp; 278 } 279 280 // entities collide 281 if (info.trigCmdCount) { 282 int sx = x / 1024; 283 int sz = z / 1024; 284 int dx = x % 1024; 285 int dz = z % 1024; 286 287 for (int i = 0; i < info.trigCmdCount; i++) { 288 TR::FloorData::TriggerCommand cmd = info.trigCmd[i]; 289 if (cmd.action == TR::Action::CAMERA_SWITCH) { 290 i++; 291 continue; 292 } 293 if (cmd.action != TR::Action::ACTIVATE) continue; 294 295 TR::Entity &e = level->entities[cmd.args]; 296 Controller *controller = (Controller*)e.controller; 297 if (!controller) continue; // Block UpdateFloor issue while entities initialization 298 299 switch (e.type) { 300 case TR::Entity::TRAP_DOOR_1 : 301 case TR::Entity::TRAP_DOOR_2 : { 302 if (!controller->isCollider()) continue; 303 304 int dirX, dirZ; 305 e.getAxis(dirX, dirZ); 306 307 int ex = int(controller->pos.x) / 1024; 308 int ey = int(controller->pos.y); 309 int ez = int(controller->pos.z) / 1024; 310 if ((ex == sx && ez == sz) || (ex + dirX == sx && ez + dirZ == sz)) { 311 if (ey >= y - 128 && controller->pos.y < info.floor) { 312 info.floor = controller->pos.y; 313 info.slantX = info.slantZ = 0; 314 info.lava = false; 315 } 316 if (ey < y - 128 && controller->pos.y > info.ceiling) 317 info.ceiling = controller->pos.y + 256; 318 } 319 break; 320 } 321 case TR::Entity::TRAP_FLOOR : { 322 if (!controller->isCollider()) continue; 323 324 if (sx != int(controller->pos.x) / 1024 || sz != int(controller->pos.z) / 1024) 325 break; 326 int ey = int(controller->pos.y) - 512; 327 if (ey >= y - 128 && ey < info.floor) { 328 info.floor = float(ey); 329 info.slantX = info.slantZ = 0; 330 info.lava = false; 331 } 332 if (ey < y - 128 && ey > info.ceiling) 333 info.ceiling = float(ey); 334 break; 335 } 336 case TR::Entity::DRAWBRIDGE : { 337 if (controller->isCollider()) continue; // drawbridge is collidable in inactive state, but it works as floor only when active 338 339 if (controller->flags.active != TR::ACTIVE) continue; 340 int dirX, dirZ; 341 e.getAxis(dirX, dirZ); 342 int ex = int(controller->pos.x) / 1024; 343 int ez = int(controller->pos.z) / 1024; 344 345 if ((ex - dirX * 1 == sx && ez - dirZ * 1 == sz) || 346 (ex - dirX * 2 == sx && ez - dirZ * 2 == sz)) { 347 int ey = int(controller->pos.y); 348 if (ey >= y - 128 && controller->pos.y < info.floor) { 349 info.floor = controller->pos.y; 350 info.slantX = info.slantZ = 0; 351 info.lava = false; 352 } 353 if (ey < y - 128 && controller->pos.y > info.ceiling) 354 info.ceiling = controller->pos.y + 256; 355 } 356 break; 357 } 358 case TR::Entity::HAMMER_HANDLE : { 359 if (!controller->isCollider()) continue; 360 361 int dirX, dirZ; 362 e.getAxis(dirX, dirZ); 363 if (abs(int(controller->pos.x) + dirX * 1024 * 3 - x) < 512 && abs(int(controller->pos.z) + dirZ * 1024 * 3 - z) < 512) { 364 info.floor -= 1024 * 3; 365 info.trigCmdCount = 0; 366 } 367 break; 368 } 369 case TR::Entity::BRIDGE_1 : 370 case TR::Entity::BRIDGE_2 : 371 case TR::Entity::BRIDGE_3 : { 372 if (sx != int(controller->pos.x) / 1024 || sz != int(controller->pos.z) / 1024) 373 break; 374 375 int s = (e.type == TR::Entity::BRIDGE_2) ? 1 : 376 (e.type == TR::Entity::BRIDGE_3) ? 2 : 0; 377 378 int ey = int(controller->pos.y), sx = 0, sz = 0; 379 380 if (s > 0) { 381 switch (e.rotation.value / 0x4000) { // get slantXZ by direction 382 case 0 : sx = s; break; 383 case 1 : sz = -s; break; 384 case 2 : sx = -s; break; 385 case 3 : sz = s; break; 386 } 387 388 ey -= sx * (sx > 0 ? (dx - 1024) : dx) >> 2; 389 ey -= sz * (sz > 0 ? (dz - 1024) : dz) >> 2; 390 } 391 392 if (y - 128 <= ey) { 393 info.floor = float(ey); 394 info.slantX = sx; 395 info.slantZ = sz; 396 info.lava = false; 397 } 398 if (ey < y - 128) 399 info.ceiling = float(ey + 64); 400 break; 401 } 402 403 default : ; 404 } 405 } 406 } 407 } 408 parseFloorDataController409 void parseFloorData(TR::Level::FloorInfo &info, int floorIndex, int dx, int dz) const { 410 if (!floorIndex) return; 411 412 TR::FloorData *fd = &level->floors[floorIndex]; 413 TR::FloorData::Command cmd; 414 415 do { 416 cmd = (*fd++).cmd; 417 418 switch (cmd.func) { 419 420 case TR::FloorData::PORTAL : 421 info.roomNext = (*fd++).value; 422 break; 423 424 case TR::FloorData::FLOOR : 425 case TR::FloorData::FLOOR_NW_SE_SOLID : 426 case TR::FloorData::FLOOR_NE_SW_SOLID : 427 case TR::FloorData::FLOOR_NW_SE_PORTAL_SE : 428 case TR::FloorData::FLOOR_NW_SE_PORTAL_NW : 429 case TR::FloorData::FLOOR_NE_SW_PORTAL_SW : 430 case TR::FloorData::FLOOR_NE_SW_PORTAL_NE : { 431 int sx, sz; 432 433 if (cmd.func == TR::FloorData::FLOOR) { 434 sx = fd->slantX; 435 sz = fd->slantZ; 436 } else { 437 if (cmd.func == TR::FloorData::FLOOR_NW_SE_SOLID || 438 cmd.func == TR::FloorData::FLOOR_NW_SE_PORTAL_SE || 439 cmd.func == TR::FloorData::FLOOR_NW_SE_PORTAL_NW) { 440 if (dx <= 1024 - dz) { 441 info.floor += cmd.triangle.b * 256; 442 sx = fd->a - fd->b; 443 sz = fd->c - fd->b; 444 } else { 445 info.floor += cmd.triangle.a * 256; 446 sx = fd->d - fd->c; 447 sz = fd->d - fd->a; 448 } 449 } else { 450 if (dx <= dz) { 451 info.floor += cmd.triangle.b * 256; 452 sx = fd->d - fd->c; 453 sz = fd->c - fd->b; 454 } else { 455 info.floor += cmd.triangle.a * 256; 456 sx = fd->a - fd->b; 457 sz = fd->d - fd->a; 458 } 459 } 460 } 461 fd++; 462 463 info.slantX = sx; 464 info.slantZ = sz; 465 info.floor -= sx * (sx > 0 ? (dx - 1023) : dx) >> 2; 466 info.floor -= sz * (sz > 0 ? (dz - 1023) : dz) >> 2; 467 break; 468 } 469 470 case TR::FloorData::CEILING : 471 case TR::FloorData::CEILING_NE_SW_SOLID : 472 case TR::FloorData::CEILING_NW_SE_SOLID : 473 case TR::FloorData::CEILING_NE_SW_PORTAL_SW : 474 case TR::FloorData::CEILING_NE_SW_PORTAL_NE : 475 case TR::FloorData::CEILING_NW_SE_PORTAL_SE : 476 case TR::FloorData::CEILING_NW_SE_PORTAL_NW : { 477 int sx, sz; 478 479 if (cmd.func == TR::FloorData::CEILING) { 480 sx = fd->slantX; 481 sz = fd->slantZ; 482 } else { 483 if (cmd.func == TR::FloorData::CEILING_NW_SE_SOLID || 484 cmd.func == TR::FloorData::CEILING_NW_SE_PORTAL_SE || 485 cmd.func == TR::FloorData::CEILING_NW_SE_PORTAL_NW) { 486 if (dx <= 1024 - dz) { 487 info.ceiling += cmd.triangle.b * 256; 488 sx = fd->c - fd->d; 489 sz = fd->b - fd->c; 490 } else { 491 info.ceiling += cmd.triangle.a * 256; 492 sx = fd->b - fd->a; 493 sz = fd->a - fd->d; 494 } 495 } else { 496 if (dx <= dz) { 497 info.ceiling += cmd.triangle.b * 256; 498 sx = fd->b - fd->a; 499 sz = fd->b - fd->c; 500 } else { 501 info.ceiling += cmd.triangle.a * 256; 502 sx = fd->c - fd->d; 503 sz = fd->a - fd->d; 504 } 505 } 506 } 507 fd++; 508 509 info.ceiling -= sx * (sx < 0 ? (dx - 1023) : dx) >> 2; 510 info.ceiling += sz * (sz > 0 ? (dz - 1023) : dz) >> 2; 511 break; 512 } 513 514 case TR::FloorData::TRIGGER : { 515 bool skip = info.trigCmdCount > 0; 516 517 if (!skip) { 518 info.trigger = (TR::Level::Trigger::Type)cmd.sub; 519 info.trigCmdCount = 0; 520 info.trigInfo = (*fd++).triggerInfo; 521 } else 522 fd++; 523 524 TR::FloorData::TriggerCommand trigCmd; 525 do { 526 trigCmd = (*fd++).triggerCmd; // trigger action 527 if (!skip) { 528 ASSERT(info.trigCmdCount < MAX_TRIGGER_COMMANDS); 529 info.trigCmd[info.trigCmdCount++] = trigCmd; 530 } 531 } while (!trigCmd.end); 532 break; 533 } 534 535 case TR::FloorData::LAVA : 536 info.lava = true; 537 break; 538 539 case TR::FloorData::CLIMB : 540 info.climb = cmd.sub; // climb mask 541 break; 542 543 case TR::FloorData::MONKEY : break; 544 case TR::FloorData::MINECART_LEFT : 545 case TR::FloorData::MINECART_RIGHT : break; 546 547 default : LOG("unknown func: %d\n", cmd.func); 548 } 549 550 } while (!cmd.end); 551 } 552 getSaveDataController553 virtual bool getSaveData(SaveEntity &data) { 554 const TR::Entity &e = getEntity(); 555 const TR::Model *m = getModel(); 556 if (entity < level->entitiesBaseCount) { 557 data.x = e.x ^ int32(pos.x); 558 data.y = e.y ^ int32(pos.y); 559 data.z = e.z ^ int32(pos.z); 560 data.rotation = e.rotation.value ^ TR::angle(normalizeAngle(angle.y)).value; 561 data.type = 0; 562 data.room = e.room ^ roomIndex; 563 } else { 564 data.x = int32(pos.x); 565 data.y = int32(pos.y); 566 data.z = int32(pos.z); 567 data.rotation = TR::angle(normalizeAngle(angle.y)).value; 568 data.type = int16(e.type); 569 data.room = uint8(roomIndex); 570 } 571 572 data.flags = e.flags.value ^ flags.value; 573 data.timer = timer == 0.0f ? 0 : (timer < 0.0f ? -1 : int16(timer * 30.0f)); 574 // animation 575 data.animIndex = m ? animation.index : 0; 576 data.animFrame = m ? animation.frameIndex : 0; 577 578 data.extraSize = 0; 579 580 return true; 581 } 582 setSaveDataController583 virtual void setSaveData(const SaveEntity &data) { 584 const TR::Entity &e = getEntity(); 585 const TR::Model *m = getModel(); 586 if (entity < level->entitiesBaseCount) { 587 pos.x = float(e.x ^ data.x); 588 pos.y = float(e.y ^ data.y); 589 pos.z = float(e.z ^ data.z); 590 angle.y = TR::angle(uint16(e.rotation.value ^ data.rotation)); 591 roomIndex = e.room ^ data.room; 592 } else { 593 pos.x = float(data.x); 594 pos.y = float(data.y); 595 pos.z = float(data.z); 596 angle.y = TR::angle(uint16(data.rotation)); 597 flags.value = data.flags; 598 roomIndex = data.room; 599 } 600 flags.value = e.flags.value ^ data.flags; 601 timer = data.timer == -1 ? -1.0f : (data.timer / 30.0f); 602 // animation 603 if (m) animation.setAnim(data.animIndex, -data.animFrame); 604 updateLights(false); 605 } 606 607 bool isActive(bool timing = true) { 608 if (flags.active != TR::ACTIVE) 609 return flags.reverse; 610 611 if (timer == 0.0f) 612 return !flags.reverse; 613 614 if (timer == -1.0f) 615 return flags.reverse; 616 617 if (timing) { 618 timer = max(0.0f, timer - Core::deltaTime); 619 620 if (timer == 0.0f) 621 timer = -1.0f; 622 } 623 624 return !flags.reverse; 625 } 626 isColliderController627 virtual bool isCollider() { 628 const TR::Entity &e = getEntity(); 629 return e.isEnemy() || 630 e.isVehicle() || 631 e.isDoor() || 632 e.type == TR::Entity::SCION_HOLDER || 633 e.type == TR::Entity::TRAP_BOULDER || 634 e.type == TR::Entity::MUTANT_EGG_SMALL; 635 } 636 activateController637 virtual bool activate() { 638 if (flags.state == TR::Entity::asActive || next) 639 return false; 640 flags.invisible = false; 641 flags.state = TR::Entity::asActive; 642 next = first; 643 first = this; 644 return true; 645 } 646 647 virtual void deactivate(bool removeFromList = false) { 648 flags.state = TR::Entity::asInactive; 649 if (removeFromList) { 650 flags.state = TR::Entity::asNone; 651 Controller *prev = NULL; 652 Controller *c = first; 653 while (c) { 654 if (c == this) { 655 if (prev) 656 prev->next = c->next; 657 else 658 first = c->next; 659 break; 660 } else 661 prev = c; 662 c = c->next; 663 } 664 next = NULL; 665 } 666 } 667 clearInactiveController668 static void clearInactive() { 669 Controller *prev = NULL; 670 Controller *c = first; 671 while (c) { 672 Controller *next = c->next; 673 if (c->flags.state == TR::Entity::asInactive) { 674 if (prev) 675 prev->next = c->next; 676 else 677 first = c->next; 678 c->flags.state = TR::Entity::asNone; 679 c->next = NULL; 680 } else 681 prev = c; 682 c = next; 683 } 684 } 685 initMeshOverridesController686 void initMeshOverrides() { 687 if (layers) return; 688 layers = new MeshLayer[MAX_LAYERS]; 689 memset(layers, 0, sizeof(MeshLayer) * MAX_LAYERS); 690 layers[0].model = getEntity().modelIndex - 1; 691 layers[0].mask = 0xFFFFFFFF; 692 } 693 694 void meshSwap(int layer, int16 model, uint32 mask = 0xFFFFFFFF) { 695 if (model < 0) return; 696 697 if (!layers) initMeshOverrides(); 698 699 TR::Model &m = level->models[model]; 700 for (int i = 0; i < m.mCount; i++) { 701 if (((1 << i) & mask) && !level->meshOffsets[m.mStart + i] && m.mStart + i > 0) 702 mask &= ~(1 << i); 703 } 704 705 layers[layer].model = model; 706 layers[layer].mask = mask; 707 } 708 709 bool aim(const vec3 &t, int joint, const vec4 &angleRange, quat &rot, quat *rotAbs = NULL) { 710 Basis b = animation.getJoints(getMatrix(), joint); 711 vec3 delta = (b.inverse() * t).normal(); 712 if (invertAim) 713 delta = -delta; 714 715 float angleY = clampAngle(atan2f(delta.x, delta.z)); 716 float angleX = clampAngle(asinf(delta.y)); 717 718 if (angleX > angleRange.x && angleX <= angleRange.y && 719 angleY > angleRange.z && angleY <= angleRange.w) { 720 721 quat ax(vec3(1, 0, 0), -angleX); 722 quat ay(vec3(0, 1, 0), angleY); 723 724 rot = ay * ax; 725 if (rotAbs) 726 *rotAbs = b.rot * rot; 727 728 return true; 729 } 730 731 if (rotAbs) 732 *rotAbs = rotYXZ(angle); 733 734 return false; 735 } 736 737 bool aim(Controller *target, int joint, const vec4 &angleRange, quat &rot, quat *rotAbs = NULL) { 738 if (target) { 739 Box box = target->getBoundingBox(); 740 vec3 t = (box.min + box.max) * 0.5f; 741 return aim(t, joint, angleRange, rot, rotAbs); 742 } 743 744 if (rotAbs) 745 *rotAbs = rotYXZ(angle); 746 747 return false; 748 } 749 750 insideRoomController751 bool insideRoom(const vec3 &pos, int roomIndex) const { 752 TR::Room &r = level->rooms[roomIndex]; 753 vec3 min = vec3((float)r.info.x + 1024, (float)r.info.yTop, (float)r.info.z + 1024); 754 vec3 max = min + vec3(float((r.xSectors - 2) * 1024), float(r.info.yBottom - r.info.yTop), float((r.zSectors - 2) * 1024)); 755 756 return pos.x >= min.x && pos.x <= max.x && 757 pos.y >= min.y && pos.y <= max.y && 758 pos.z >= min.z && pos.z <= max.z; 759 } 760 getModelController761 virtual const TR::Model* getModel() { 762 int index = getEntity().modelIndex; 763 return index > 0 ? &level->models[index - 1] : NULL; 764 } 765 getEntityController766 const TR::Entity& getEntity() const { 767 return level->entities[entity]; 768 } 769 getRoomController770 TR::Room& getRoom() { 771 int index = getRoomIndex(); 772 ASSERT(index >= 0 && index < level->roomsCount); 773 return level->rooms[index]; 774 } 775 getRoomIndexController776 virtual int getRoomIndex() const { 777 return roomIndex; 778 } 779 getPosController780 virtual vec3 getPos() { 781 return getEntity().isActor() ? getJoint(0).pos : pos; 782 } 783 getDirController784 vec3 getDir() const { 785 return vec3(angle.x, angle.y); 786 } 787 applyGravityController788 static inline void applyGravity(float &speed) { 789 speed += (speed < 128.0f ? GRAVITY : 1.0f) * (30.0f * Core::deltaTime); 790 } 791 792 bool alignToWall(float offset = 0.0f, int quadrant = -1, int maxDist = 0, int maxWidth = 0) { 793 int q = angleQuadrant(angle.y, 0.25f); 794 int ix = int(pos.x); 795 int iz = int(pos.z); 796 int x = ix & ~1023; 797 int z = iz & ~1023; 798 799 if (quadrant > -1 && quadrant != q) 800 return false; 801 802 if (maxDist) { // check dist 803 int dist = 0; 804 switch (q) { 805 case 0 : dist = z + 1024 - iz; break; 806 case 1 : dist = x + 1024 - ix; break; 807 case 2 : dist = iz - z; break; 808 case 3 : dist = ix - x; break; 809 } 810 if (dist > maxDist) 811 return false; 812 } 813 814 if (maxWidth) { 815 int width = abs( ((q % 2) ? (iz - z) : (ix - x)) - 512); 816 if (width > maxWidth) 817 return false; 818 } 819 820 switch (q) { 821 case 0 : pos.z = z + 1024 + offset; break; 822 case 1 : pos.x = x + 1024 + offset; break; 823 case 2 : pos.z = z - offset; break; 824 case 3 : pos.x = x - offset; break; 825 } 826 827 angle.y = q * (PI * 0.5f); 828 return true; 829 } 830 getBoundingBoxController831 Box getBoundingBox() { 832 return getBoundingBoxLocal() * getMatrix(); 833 } 834 835 Box getBoundingBoxLocal(bool oriented = false) { 836 return animation.getBoundingBox(vec3(0, 0, 0), oriented ? getEntity().rotation.value / 0x4000 : 0); 837 } 838 getSpheresController839 int getSpheres(Sphere *spheres) { 840 const TR::Model *m = getModel(); 841 ASSERT(m->mCount <= MAX_JOINTS); 842 843 int jFrame = jointsFrame; 844 updateJoints(); 845 jointsFrame = jFrame; 846 847 int count = 0; 848 for (int i = 0; i < m->mCount; i++) { 849 TR::Mesh &aMesh = level->meshes[level->meshOffsets[m->mStart + i]]; 850 if (aMesh.radius <= 0) continue; 851 vec3 center = joints[i] * aMesh.center; 852 spheres[count++] = Sphere(center, aMesh.radius); 853 } 854 return count; 855 } 856 857 Box getSpheresBox(bool local = false) { 858 Sphere spheres[MAX_JOINTS]; 859 int count = getSpheres(spheres); 860 if (count) { 861 862 if (local) { 863 mat4 m = getMatrix().inverseOrtho(); 864 for (int i = 0; i < count; i++) 865 spheres[i].center = m * spheres[i].center; 866 } 867 868 Box box(spheres[0].center - vec3(spheres[0].radius), spheres[0].center + vec3(spheres[0].radius)); 869 for (int i = 1; i < count; i++) 870 box += Box(spheres[i].center - vec3(spheres[i].radius), spheres[i].center + vec3(spheres[i].radius)); 871 872 return box; 873 } else 874 return local ? getBoundingBoxLocal() : getBoundingBox(); 875 } 876 877 878 int collide(Controller *controller, bool checkBoxes = true) { 879 const TR::Model *a = getModel(); 880 const TR::Model *b = controller->getModel(); 881 if (!a || !b) 882 return 0; 883 884 if (checkBoxes && !getBoundingBox().intersect(controller->getBoundingBox())) 885 return 0; 886 887 ASSERT(a->mCount <= 34); 888 ASSERT(b->mCount <= 34); 889 890 Sphere aSpheres[MAX_JOINTS]; 891 Sphere bSpheres[MAX_JOINTS]; 892 893 int aCount = getSpheres(aSpheres); 894 int bCount = controller->getSpheres(bSpheres); 895 896 int mask = 0; 897 for (int i = 0; i < aCount; i++) 898 for (int j = 0; j < bCount; j++) 899 if (bSpheres[j].intersect(aSpheres[i])) { 900 mask |= (1 << i); 901 break; 902 } 903 return mask; 904 } 905 collideController906 bool collide(const Sphere &sphere) { 907 return getBoundingBoxLocal().intersect(Sphere(getMatrix().inverseOrtho() * sphere.center, sphere.radius)); 908 } 909 traceController910 vec3 trace(int fromRoom, const vec3 &from, const vec3 &to, int &room, bool isCamera) { // TODO: use Bresenham 911 room = fromRoom; 912 913 vec3 pos = from, dir = to - from; 914 int px = (int)pos.x, py = (int)pos.y, pz = (int)pos.z; 915 916 float dist = dir.length(); 917 dir = dir * (1.0f / dist); 918 919 int lr = -1, lx = -1, lz = -1; 920 TR::Level::FloorInfo info; 921 while (dist > 1.0f) { 922 int sx = px / 1024 * 1024 + 512, 923 sz = pz / 1024 * 1024 + 512; 924 925 if (lr != room || lx != sx || lz != sz) { 926 getFloorInfo(room, vec3(float(sx), float(py), float(sz)), info); 927 if (info.roomNext != TR::NO_ROOM) { 928 room = info.roomNext; 929 getFloorInfo(room, vec3(float(sx), float(py), float(sz)), info); 930 } 931 lr = room; 932 lx = sx; 933 lz = sz; 934 } 935 936 if (isCamera) { 937 if (py > info.floor && info.roomBelow != TR::NO_ROOM) 938 room = info.roomBelow; 939 else if (py < info.ceiling && info.roomAbove != TR::NO_ROOM) 940 room = info.roomAbove; 941 else if (py > info.floor || py < info.ceiling) { 942 int minX = px / 1024 * 1024; 943 int minZ = pz / 1024 * 1024; 944 int maxX = minX + 1024; 945 int maxZ = minZ + 1024; 946 947 pos = vec3(float(clamp(px, minX, maxX)), pos.y, float(clamp(pz, minZ, maxZ))) + boxNormal(px, pz) * 256.0f; 948 dir = (pos - from).normal(); 949 } 950 } else { 951 if (py > info.floor) { 952 if (info.roomBelow != TR::NO_ROOM) 953 room = info.roomBelow; 954 else 955 break; 956 } 957 958 if (py < info.ceiling) { 959 if (info.roomAbove != TR::NO_ROOM) 960 room = info.roomAbove; 961 else 962 break; 963 } 964 } 965 966 float d = min(dist, 32.0f); // STEP = 32 967 dist -= d; 968 pos = pos + dir * d; 969 970 px = (int)pos.x; 971 py = (int)pos.y; 972 pz = (int)pos.z; 973 } 974 975 return pos; 976 } 977 traceXController978 int traceX(const TR::Location &from, TR::Location &to) { 979 vec3 d = to.pos - from.pos; 980 if (fabsf(d.x) < EPS) return 1; 981 982 d.yz() *= 1024 / d.x; 983 984 vec3 p = from.pos; 985 986 p.x = float(int(p.x) / 1024 * 1024); 987 if (d.x > 0) p.x += 1023; 988 989 p.yz() += d.yz() * ((p.x - from.pos.x) / 1024); 990 991 float s = float(sign(d.x)); 992 d.x = 1024; 993 d *= s; 994 995 int16 roomIndex = from.room; 996 while ((p.x - to.pos.x) * s < 0) { 997 if (level->isBlocked(roomIndex, p)) { 998 to.pos = p; 999 to.room = roomIndex; 1000 return -1; 1001 } 1002 1003 to.room = roomIndex; 1004 if (level->isBlocked(roomIndex, vec3(p.x + s, p.y, p.z))) { 1005 to.pos = p; 1006 return 0; 1007 } 1008 1009 p += d; 1010 } 1011 1012 to.room = roomIndex; 1013 return 1; 1014 } 1015 traceZController1016 int traceZ(const TR::Location &from, TR::Location &to) { 1017 vec3 d = to.pos - from.pos; 1018 if (fabsf(d.z) < EPS) return 1; 1019 1020 d.xy() *= 1024 / d.z; 1021 1022 vec3 p = from.pos; 1023 1024 p.z = float(int(p.z) / 1024 * 1024); 1025 if (d.z > 0) p.z += 1023; 1026 1027 p.xy() += d.xy() * ((p.z - from.pos.z) / 1024); 1028 1029 float s = float(sign(d.z)); 1030 d.z = 1024; 1031 d *= s; 1032 1033 int16 roomIndex = from.room; 1034 while ((p.z - to.pos.z) * s < 0) { 1035 if (level->isBlocked(roomIndex, p)) { 1036 to.pos = p; 1037 to.room = roomIndex; 1038 return -1; 1039 } 1040 1041 to.room = roomIndex; 1042 if (level->isBlocked(roomIndex, vec3(p.x, p.y, p.z + s))) { 1043 to.pos = p; 1044 return 0; 1045 } 1046 1047 p += d; 1048 } 1049 1050 to.room = roomIndex; 1051 return 1; 1052 } 1053 traceController1054 bool trace(const TR::Location &from, TR::Location &to) { 1055 int rx, rz; 1056 1057 if (fabsf(to.pos.x - from.pos.x) < fabsf(to.pos.z - from.pos.z)) { 1058 rz = traceZ(from, to); 1059 if (!rz) return false; 1060 rx = traceX(from, to); 1061 } else { 1062 rx = traceX(from, to); 1063 if (!rx) return false; 1064 rz = traceZ(from, to); 1065 } 1066 TR::Room::Sector *sector = level->getSector(to.room, to.pos); 1067 return clipHeight(from, to, sector) && rx == 1 && rz == 1; 1068 } 1069 clipHeightController1070 bool clipHeight(const TR::Location &from, TR::Location &to, TR::Room::Sector *sector) { 1071 vec3 dir = to.pos - from.pos; 1072 1073 float y = level->getFloor(sector, to.pos); 1074 if (to.pos.y <= y || from.pos.y >= y) { 1075 y = level->getCeiling(sector, to.pos); 1076 if (to.pos.y >= y || from.pos.y <= y) 1077 return true; 1078 } 1079 1080 ASSERT(dir.y != 0.0f); 1081 1082 float d = (y - from.pos.y) / dir.y; 1083 to.pos.y = y; 1084 to.pos.x = from.pos.x + dir.x * d; 1085 to.pos.z = from.pos.z + dir.z * d; 1086 return false; 1087 } 1088 1089 checkRangeController1090 bool checkRange(Controller *target, float range) { 1091 vec3 d = target->pos - pos; 1092 return fabsf(d.x) < range && fabsf(d.z) < range && fabsf(d.y) < range; 1093 } 1094 updateRoomController1095 void updateRoom() { 1096 level->getSector(roomIndex, pos); 1097 level->getWaterInfo(getRoomIndex(), pos, waterLevel, waterDepth); 1098 } 1099 1100 virtual void hit(float damage, Controller *enemy = NULL, TR::HitType hitType = TR::HIT_DEFAULT) {} doCustomCommandController1101 virtual void doCustomCommand (int curFrame, int prevFrame) {} applyFlowController1102 virtual void applyFlow(TR::Camera &sink) {} 1103 cmdOffsetController1104 virtual void cmdOffset(const vec3 &offset) { 1105 pos = pos + offset.rotateY(-angle.y); 1106 updateRoom(); 1107 } 1108 cmdJumpController1109 virtual void cmdJump(const vec3 &vel) {} cmdEmptyController1110 virtual void cmdEmpty() {} 1111 cmdEffectController1112 virtual void cmdEffect(int fx) { 1113 switch (fx) { 1114 case TR::Effect::MESH_SWAP_1 : 1115 case TR::Effect::MESH_SWAP_2 : 1116 case TR::Effect::MESH_SWAP_3 : { 1117 if (!layers) initMeshOverrides(); 1118 uint32 mask = (layers[1].mask == 0xFFFFFFFF) ? 0 : 0xFFFFFFFF; 1119 meshSwap(1, level->extra.meshSwap[fx - TR::Effect::MESH_SWAP_1], mask); 1120 break; 1121 } 1122 case TR::Effect::INV_ON : flags.invisible = true; break; 1123 case TR::Effect::INV_OFF : flags.invisible = false; break; 1124 case TR::Effect::DYN_ON : 1125 Core::lightColor[1] = vec4(0.6f, 0.5f, 0.1f, 1.0f / 4096.0f); 1126 Core::lightPos[1] = getPos(); 1127 break; 1128 case TR::Effect::DYN_OFF : 1129 Core::lightColor[1] = vec4(0, 0, 0, 1); 1130 break; 1131 case TR::Effect::FOOTPRINT : break; // TODO TR3 1132 default : ASSERT(false); 1133 } 1134 } 1135 updateAnimationController1136 virtual void updateAnimation(bool commands) { 1137 animation.update(); 1138 1139 if (level->isCutsceneLevel() && getEntity().isActor()) { 1140 vec3 p = getPos(); 1141 if ((p - lastPos).length2() > 256 * 256) { 1142 game->waterDrop(p, 96.0, 0.1f); 1143 lastPos = p; 1144 } 1145 } 1146 1147 TR::Animation *anim = animation; 1148 1149 // apply animation commands 1150 if (commands) { 1151 int16 *ptr = &level->commands[anim->animCommand]; 1152 1153 for (int i = 0; i < anim->acCount; i++) { 1154 int cmd = *ptr++; 1155 switch (cmd) { 1156 case TR::ANIM_CMD_OFFSET : ptr += 3; break; 1157 case TR::ANIM_CMD_JUMP : ptr += 2; break; 1158 case TR::ANIM_CMD_EMPTY : cmdEmpty(); break; 1159 case TR::ANIM_CMD_KILL : 1160 if (animation.isEnded) 1161 deactivate(); 1162 break; 1163 case TR::ANIM_CMD_SOUND : 1164 case TR::ANIM_CMD_EFFECT : { 1165 int frame = (*ptr++) - anim->frameStart; 1166 int16 sfx = (*ptr++); 1167 int fx = sfx & 0x3FFF; 1168 if (animation.isFrameActive(frame)) { 1169 if (cmd == TR::ANIM_CMD_EFFECT) { 1170 switch (fx) { 1171 case TR::Effect::ROTATE_180 : angle.x = -angle.x; angle.y = angle.y + PI; break; 1172 case TR::Effect::FLOOR_SHAKE : game->setEffect(this, TR::Effect::Type(fx)); break; 1173 case TR::Effect::FINISH_LEVEL : game->loadNextLevel(); break; 1174 case TR::Effect::FLIP_MAP : game->flipMap(); break; 1175 default : cmdEffect(fx); break; 1176 } 1177 } else { 1178 if (!(level->version & TR::VER_TR1)) { 1179 if ((sfx & 0x8000) && waterDepth <= 0.0f) 1180 break; 1181 if ((sfx & 0x4000) && waterDepth > 0.0f) 1182 break; 1183 } 1184 game->playSound(fx, pos, Sound::PAN); 1185 } 1186 } 1187 break; 1188 } 1189 } 1190 } 1191 } 1192 1193 if (animation.frameIndex != animation.framePrev) 1194 doCustomCommand(animation.frameIndex, animation.framePrev); 1195 1196 if (animation.isEnded) { // if animation is end - switch to next 1197 if (animation.offset != 0.0f) cmdOffset(animation.offset); 1198 if (animation.jump != 0.0f) cmdJump(animation.jump); 1199 animation.playNext(); 1200 } else 1201 animation.framePrev = animation.frameIndex; 1202 } 1203 checkNearController1204 bool checkNear(const vec3 &p, float dist) { 1205 vec3 d = p - pos; 1206 if (d.x < -dist || d.x > dist || d.z < -dist || d.z > dist || d.y < -3072.0f || d.y > 3072.0f || (SQR(d.x) + SQR(d.z) > SQR(dist))) 1207 return false; 1208 Box box = getBoundingBoxLocal(); 1209 if (d.y < box.min.y || (d.y - 100.0f) > box.max.y) 1210 return false; 1211 return true; 1212 } 1213 updateExplosionController1214 void updateExplosion() { 1215 if (!explodeMask) return; 1216 const TR::Model *model = getModel(); 1217 for (int i = 0; i < model->mCount; i++) 1218 if (explodeMask & (1 << i)) { 1219 ExplodePart &part = explodeParts[i]; 1220 applyGravity(part.velocity.y); 1221 1222 quat q = quat(vec3(1, 0, 0), PI * Core::deltaTime) * quat(vec3(0, 0, 1), PI * 2.0f * Core::deltaTime); 1223 part.basis = Basis(part.basis.rot * q, part.basis.pos + part.velocity * (30.0f * Core::deltaTime)); 1224 1225 vec3 p = part.basis.pos; 1226 1227 TR::Room::Sector *sector = level->getSector(part.roomIndex, p); 1228 float ceiling = level->getCeiling(sector, p); 1229 float floor = level->getFloor(sector, p); 1230 1231 bool explode = false; 1232 1233 if (p.y < ceiling) { 1234 p.y = ceiling; 1235 part.velocity.y = -part.velocity.y; 1236 } 1237 1238 if (p.y >= floor) { 1239 p.y = floor; 1240 part.damage = 0.0f; 1241 explode = true; 1242 } 1243 1244 if (part.damage > 0.0f) { 1245 Controller *lara = game->getLara(); 1246 if (lara->checkNear(p, part.damage * 2.0f)) { 1247 lara->hit(part.damage); 1248 explode = true; 1249 } 1250 } 1251 1252 if (explode) { 1253 explodeMask &= ~(1 << i); 1254 game->addEntity(TR::Entity::EXPLOSION, part.roomIndex, p); 1255 } 1256 } 1257 1258 if (!explodeMask) { 1259 delete[] explodeParts; 1260 explodeParts = NULL; 1261 } 1262 } 1263 updateController1264 virtual void update() { 1265 if (getEntity().modelIndex <= 0) 1266 return; 1267 1268 if (explodeMask) 1269 updateExplosion(); 1270 else 1271 updateAnimation(true); 1272 updateLights(true); 1273 } 1274 getLightRoomController1275 virtual TR::Room& getLightRoom() { 1276 return getRoom(); 1277 } 1278 1279 #define LIGHT_DIST 8192.0f 1280 1281 void updateLights(bool lerp = true) { 1282 const TR::Room &room = getLightRoom(); 1283 1284 targetLight = NULL; 1285 1286 if (getEntity().intensity == -1) { 1287 int ambient = room.ambient; 1288 1289 if (room.lightsCount && getModel()) { 1290 vec3 center = getBoundingBox().center(); 1291 1292 int x = int(center.x); 1293 int y = int(center.y); 1294 int z = int(center.z); 1295 1296 ambient = room.getAmbient(x, y, z, &targetLight); 1297 } 1298 1299 intensity = intensityf(ambient); 1300 } 1301 1302 if (targetLight == NULL) { 1303 mainLightPos = vec3(0); 1304 mainLightColor = vec4(0, 0, 0, 1); 1305 return; 1306 } 1307 1308 vec3 tpos = vec3(float(targetLight->x), float(targetLight->y), float(targetLight->z)); 1309 vec4 tcolor = vec4(vec3(targetLight->color.r, targetLight->color.g, targetLight->color.b) * (1.0f / 255.0f), float(targetLight->radius)); 1310 1311 if (mainLightFlip != level->state.flags.flipped) { 1312 lerp = false; 1313 mainLightFlip = level->state.flags.flipped; 1314 } 1315 1316 if (lerp) { 1317 float t = Core::deltaTime * 2.0f; 1318 mainLightPos = mainLightPos.lerp(tpos, t); 1319 mainLightColor = mainLightColor.lerp(tcolor, t); 1320 } else { 1321 mainLightPos = tpos; 1322 mainLightColor = tcolor; 1323 } 1324 1325 // fix position and radius 1326 mainLightColor.w = min(LIGHT_DIST * 1.5f, mainLightColor.w); 1327 vec3 dir = mainLightPos - pos; 1328 float dist = dir.length(); 1329 if (dist > LIGHT_DIST) { 1330 dir *= (LIGHT_DIST / dist); 1331 mainLightPos = pos + dir; 1332 } 1333 } 1334 getMatrixController1335 mat4 getMatrix() { 1336 const TR::Entity &e = getEntity(); 1337 1338 if (level->isCutsceneLevel() && (e.isActor() || e.isLara()) && e.type != TR::Entity::CUT_4) 1339 return level->cutMatrix; 1340 1341 if (!lockMatrix) { 1342 matrix.identity(); 1343 matrix.translate(pos); 1344 if (angle.y != 0.0f) matrix.rotateY(angle.y - (animation.anims != NULL ? (animation.rot * animation.delta) : 0.0f)); 1345 if (angle.x != 0.0f) matrix.rotateX(angle.x); 1346 if (angle.z != 0.0f) matrix.rotateZ(angle.z); 1347 } 1348 return matrix; 1349 } 1350 explodeController1351 void explode(int32 mask, float damage) { 1352 const TR::Model *model = getModel(); 1353 1354 if (!layers) initMeshOverrides(); 1355 1356 mask &= layers[0].mask; 1357 layers[0].mask &= ~mask; 1358 1359 explodeParts = new ExplodePart[model->mCount]; 1360 explodeMask = 0; 1361 1362 updateJoints(); 1363 1364 vec3 laraPos = game->getLara(pos)->pos; 1365 1366 int roomIndex = getRoomIndex(); 1367 for (int i = 0; i < model->mCount; i++) { 1368 if (!(mask & (1 << i))) 1369 continue; 1370 explodeMask |= (1 << i); 1371 float angle = randf() * PI * 2.0f; 1372 vec2 speed = vec2(randf(), -randf()) * (getEntity().type == TR::Entity::ENEMY_GIANT_MUTANT ? 256.0f : 128.0f); 1373 1374 if ((rand() % 4) == 0) { 1375 angle = (laraPos - joints[i].pos).angleY(); 1376 } 1377 1378 ExplodePart &part = explodeParts[i]; 1379 part.basis = joints[i]; 1380 part.basis.w = 1.0f; 1381 part.velocity = vec3(cosf(angle) * speed.x, speed.y, sinf(angle) * speed.x); 1382 part.damage = damage; 1383 part.roomIndex = roomIndex; 1384 } 1385 } 1386 renderShadowController1387 void renderShadow(MeshBuilder *mesh) { 1388 if (Core::pass != Core::passCompose || level->isCutsceneLevel()) 1389 return; 1390 1391 Box boxL = getBoundingBoxLocal(); 1392 Box boxA = boxL * getMatrix(); 1393 1394 vec3 center = boxA.center(); 1395 1396 TR::Level::FloorInfo info; 1397 getFloorInfo(getRoomIndex(), center, info); 1398 1399 const vec3 size = boxL.size() * (1.0f / 1024.0f); 1400 1401 vec3 dir = getDir(); 1402 vec3 up = info.getNormal(); 1403 vec3 right = dir.cross(up).normal(); 1404 dir = up.cross(right).normal(); 1405 1406 mat4 m; 1407 m.identity(); 1408 m.dir() = vec4(dir * size.z, 0.0f); 1409 m.up() = vec4(up, 0.0f); 1410 m.right() = vec4(right * size.x, 0.0f); 1411 m.offset() = vec4(center.x, info.floor - 8.0f, center.z, 1.0f); 1412 Core::mModel = m; 1413 1414 Basis b; 1415 b.identity(); 1416 1417 game->setShader(Core::pass, Shader::FLASH, false, false); 1418 Core::active.shader->setParam(uViewProj, Core::mViewProj * m); 1419 Core::setBasis(&b, 1); 1420 1421 float alpha = lerp(0.7f, 0.9f, clamp((info.floor - boxA.max.y) / 1024.0f, 0.0f, 1.0f) ); 1422 float lum = 1.0f - alpha; 1423 Core::setMaterial(lum, lum, lum, alpha); 1424 1425 Core::setDepthWrite(false); 1426 mesh->renderShadowBlob(); 1427 Core::setDepthWrite(true); 1428 } 1429 updateJointsController1430 void updateJoints() { 1431 if (Core::stats.frame == jointsFrame) 1432 return; 1433 animation.getJoints(getMatrix(), -1, true, joints); 1434 jointsFrame = Core::stats.frame; 1435 } 1436 getJointController1437 Basis& getJoint(int index) { 1438 updateJoints(); 1439 1440 ASSERT(getModel() && index < getModel()->mCount); 1441 if (getModel() && getModel()->mCount && index >= getModel()->mCount) 1442 return joints[0]; 1443 1444 return joints[index]; 1445 } 1446 renderSpriteController1447 void renderSprite(int frame) { 1448 TR::Entity::Type type = getEntity().type; 1449 float fDiffuse = TR::Entity::isPickup(type) ? 1.0f : 0.5f; 1450 float fAmbient = (intensity < 0.0f ? intensityf(getRoom().ambient) : intensity) * fDiffuse; 1451 float fAlpha = (type == TR::Entity::SMOKE || type == TR::Entity::WATER_SPLASH || type == TR::Entity::SPARKLES) ? 0.75f : 1.0f; 1452 1453 uint8 ambient = clamp(int(fAmbient * 255.0f), 0, 255); 1454 uint8 alpha = clamp(int(fAlpha * 255.0f), 0, 255); 1455 Color32 color(ambient, ambient, ambient, alpha); 1456 1457 vec3 p = pos - Core::viewPos.xyz(); 1458 1459 game->getMesh()->addDynSprite(level->spriteSequences[-(getEntity().modelIndex + 1)].sStart + frame, short3(int16(p.x), int16(p.y), int16(p.z)), false, false, color, color); 1460 } 1461 renderController1462 virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { 1463 if (getEntity().modelIndex < 0) { 1464 renderSprite(0); 1465 return; 1466 } 1467 ASSERT(getEntity().modelIndex > 0); 1468 1469 const TR::Model *model = getModel(); 1470 1471 mat4 matrix = getMatrix(); 1472 1473 Box box = animation.getBoundingBox(vec3(0, 0, 0), 0); 1474 if (!explodeMask && frustum && !frustum->isVisible(matrix, box.min, box.max)) 1475 return; 1476 1477 ASSERT(model); 1478 1479 flags.rendered = true; 1480 1481 updateJoints(); 1482 1483 Core::mModel = matrix; 1484 1485 if (layers) { 1486 uint32 mask = 0; 1487 1488 for (int i = MAX_LAYERS - 1; i >= 0; i--) { 1489 uint32 vmask = (layers[i].mask & ~mask) | (!i ? explodeMask : 0); 1490 vmask &= visibleMask; 1491 if (!vmask) continue; 1492 mask |= layers[i].mask; 1493 // set meshes visibility 1494 for (int j = 0; j < model->mCount; j++) 1495 joints[j].w = (vmask & (1 << j)) ? 1.0f : 0.0f; // hide invisible parts 1496 1497 if (explodeMask) { 1498 ASSERT(explodeParts); 1499 const TR::Model *model = getModel(); 1500 for (int i = 0; i < model->mCount; i++) 1501 if (explodeMask & (1 << i)) 1502 joints[i] = explodeParts[i].basis; 1503 } 1504 1505 // render 1506 Core::setBasis(joints, model->mCount); 1507 mesh->renderModel(layers[i].model, caustics); 1508 } 1509 } else { 1510 Core::setBasis(joints, model->mCount); 1511 mesh->renderModel(model->index, caustics); 1512 } 1513 } 1514 addRicochetController1515 void addRicochet(const vec3 &pos, bool sound) { 1516 game->addEntity(TR::Entity::RICOCHET, getRoomIndex(), pos); 1517 if (sound) 1518 game->playSound(TR::SND_RICOCHET, pos, Sound::PAN); 1519 } 1520 }; 1521 1522 struct DummyController : Controller { 1523 DummyControllerDummyController1524 DummyController(IGame *game, int entity) : Controller(game, entity) {} 1525 updateDummyController1526 virtual void update() {} 1527 }; 1528 1529 1530 Controller *Controller::first = NULL; 1531 1532 #endif 1533