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