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