1 #ifndef H_CHARACTER
2 #define H_CHARACTER
3 
4 #include "controller.h"
5 #include "collision.h"
6 #include "sprite.h"
7 
8 struct Character : Controller {
9     float   health;
10     float   tilt;
11     quat    rotHead, rotChest;
12 
13     enum Stand {
14         STAND_AIR,
15         STAND_GROUND,
16         STAND_SLIDE,
17         STAND_HANG,
18         STAND_UNDERWATER,
19         STAND_ONWATER,
20         STAND_WADE
21     }       stand;
22 
23     int     input, lastInput;
24 
25     enum Key {
26         LEFT        = 1 << 1,
27         RIGHT       = 1 << 2,
28         FORTH       = 1 << 3,
29         BACK        = 1 << 4,
30         JUMP        = 1 << 5,
31         WALK        = 1 << 6,
32         ACTION      = 1 << 7,
33         WEAPON      = 1 << 8,
34         LOOK        = 1 << 9,
35         DEATH       = 1 << 10
36     };
37 
38     Controller  *viewTarget;
39     int         jointChest;
40     int         jointHead;
41     vec4        rangeChest;
42     vec4        rangeHead;
43 
44     vec3    velocity;
45     float   angleExt;
46     float   speed;
47     float   lookAtSpeed;
48     int     stepHeight;
49     int     dropHeight;
50 
51     int     zone;
52     int     box;
53 
54     bool    burn;
55     bool    flying;
56     bool    fullChestRotation;
57 
58     Collision collision;
59 
CharacterCharacter60     Character(IGame *game, int entity, float health) : Controller(game, entity), health(health), tilt(0.0f), stand(STAND_GROUND), lastInput(0), viewTarget(NULL), jointChest(-1), jointHead(-1), velocity(0.0f), angleExt(0.0f), speed(0.0f) {
61         stepHeight =  256;
62         dropHeight = -256;
63 
64         lookAtSpeed = 8.0f;
65 
66         rangeChest = vec4(-0.80f, 0.80f, -0.75f, 0.75f) * PI;
67         rangeHead  = vec4(-0.25f, 0.25f, -0.50f, 0.50f) * PI;
68         animation.initOverrides();
69 
70         rotHead  = rotChest = quat(0, 0, 0, 1);
71 
72         burn   = false;
73         flying = getEntity().type == TR::Entity::ENEMY_BAT;
74         fullChestRotation = false;
75         updateZone();
76     }
77 
isActiveTargetCharacter78     bool isActiveTarget() {
79         return flags.state == TR::Entity::asActive && !flags.invisible && health > 0.0f;
80     }
81 
getRoomIndexCharacter82     virtual int getRoomIndex() const {
83         int index = Controller::getRoomIndex();
84 
85         if (level->isCutsceneLevel())
86             return index;
87         return index;
88     }
89 
getLightRoomCharacter90     virtual TR::Room& getLightRoom() {
91         if (stand == STAND_ONWATER) {
92             int16 rIndex = getRoomIndex();
93             TR::Room::Sector *sector = level->getSector(rIndex, pos);
94             if (sector && sector->roomAbove != TR::NO_ROOM)
95                 return level->rooms[sector->roomAbove];
96         }
97         return Controller::getLightRoom();
98     }
99 
updateZoneCharacter100     bool updateZone() {
101         if (level->isCutsceneLevel())
102             return false;
103 
104         int dx, dz;
105         TR::Room::Sector &s = level->getSector(getRoomIndex(), int(pos.x), int(pos.z), dx, dz);
106         if (s.boxIndex == TR::NO_BOX)
107             return false;
108         box  = s.boxIndex;
109         zone = getZones()[box];
110         return true;
111     }
112 
getZonesCharacter113     uint16* getZones() {
114         TR::Zone &zones = level->zones[level->state.flags.flipped];
115         return (flying || stand == STAND_UNDERWATER || stand == STAND_ONWATER) ? zones.fly : (stepHeight == 256 ? zones.ground1 : zones.ground2);
116     }
117 
rotateYCharacter118     void rotateY(float delta) {
119         angle.y = clampAngle(angle.y + delta);
120         velocity = velocity.rotateY(-delta);
121     }
122 
rotateXCharacter123     void rotateX(float delta) {
124         angle.x = clamp(angle.x + delta, -PI * 0.49f, PI * 0.49f);
125     }
126 
127     virtual void hit(float damage, Controller *enemy = NULL, TR::HitType hitType = TR::HIT_DEFAULT) {
128         if (getEntity().isEnemy() && health > 0.0f && health <= damage)
129             saveStats.kills++;
130         health = max(0.0f, health - damage);
131     }
132 
updateVelocityCharacter133     virtual void  updateVelocity()      {}
updatePositionCharacter134     virtual void  updatePosition()      {}
getStandCharacter135     virtual Stand getStand()            { return stand; }
getHeightCharacter136     virtual int   getHeight()           { return 0; }
getStateAirCharacter137     virtual int   getStateAir()         { return state; }
getStateGroundCharacter138     virtual int   getStateGround()      { return state; }
getStateSlideCharacter139     virtual int   getStateSlide()       { return state; }
getStateHangCharacter140     virtual int   getStateHang()        { return state; }
getStateUnderwaterCharacter141     virtual int   getStateUnderwater()  { return state; }
getStateOnwaterCharacter142     virtual int   getStateOnwater()     { return state; }
getStateWadeCharacter143     virtual int   getStateWade()        { return state; }
getStateDeathCharacter144     virtual int   getStateDeath()       { return state; }
getStateDefaultCharacter145     virtual int   getStateDefault()     { return state; }
getInputCharacter146     virtual int   getInput()            { return health <= 0 ? DEATH : 0; }
useHeadAnimationCharacter147     virtual bool  useHeadAnimation()    { return false; }
148 
getNextStateCharacter149     int getNextState() {
150         if (input & DEATH) {
151             int deathState = getStateDeath();
152             if (state == deathState || animation.canSetState(deathState)) {
153                 if (stand != STAND_AIR)
154                     velocity = vec3(0.0f);
155                 return deathState;
156             }
157         }
158 
159         switch (stand) {
160             case STAND_AIR        : return getStateAir();
161             case STAND_GROUND     : return getStateGround();
162             case STAND_SLIDE      : return getStateSlide();
163             case STAND_HANG       : return getStateHang();
164             case STAND_UNDERWATER : return getStateUnderwater();
165             case STAND_ONWATER    : return getStateOnwater();
166             case STAND_WADE       : return getStateWade();
167         }
168         return animation.state;
169     }
170 
updateStateCharacter171     virtual void updateState() {
172         if (stand == STAND_UNDERWATER || stand == STAND_ONWATER)
173             burn = false;
174 
175         int state = getNextState();
176         // try to set new state
177         if (!animation.setState(state))
178             animation.setState(getStateDefault());
179     }
180 
updateTiltCharacter181     virtual void updateTilt(float value, float tiltSpeed, float tiltMax) {
182         value = clamp(value, -tiltMax, +tiltMax);
183         decrease(value - angle.z, angle.z, tiltSpeed);
184     }
185 
updateTiltCharacter186     virtual void updateTilt(bool active, float tiltSpeed, float tiltMax) {
187     // calculate turning tilt
188         if (active && (input & (LEFT | RIGHT)) && (tilt == 0.0f || (tilt < 0.0f && (input & LEFT)) || (tilt > 0.0f && (input & RIGHT)))) {
189             if (input & LEFT)  tilt -= tiltSpeed;
190             if (input & RIGHT) tilt += tiltSpeed;
191             tilt = clamp(tilt, -tiltMax, +tiltMax);
192         } else {
193             if (tilt > 0.0f) tilt = max(0.0f, tilt - tiltSpeed);
194             if (tilt < 0.0f) tilt = min(0.0f, tilt + tiltSpeed);
195         }
196         angle.z = tilt;
197     }
198 
isPressedCharacter199     bool isPressed(Key key) {
200         return (input & key) && !(lastInput & key);
201     }
202 
updateCharacter203     virtual void update() {
204         updateRoom();
205 
206         vec3 p = pos;
207         lastInput = input;
208         input = getInput();
209         stand = getStand();
210         updateState();
211         Controller::update();
212 
213         if (flags.active) {
214             updateVelocity();
215             updatePosition();
216             if (p != pos) {
217                 if (updateZone())
218                     updateLights();
219                 else
220                     pos = p;
221             }
222         }
223     }
224 
cmdJumpCharacter225     virtual void cmdJump(const vec3 &vel) {
226         velocity.x = sinf(angleExt) * vel.z;
227         velocity.y = vel.y;
228         velocity.z = cosf(angleExt) * vel.z;
229         stand = STAND_AIR;
230     }
231 
getViewPointCharacter232     vec3 getViewPoint() { // TOOD: remove this
233         return getJoint(jointChest).pos;
234     }
235 
lookAtCharacter236     virtual void lookAt(Controller *target) {
237         if (health <= 0.0f)
238             target = NULL;
239 
240         vec3 t(0.0f);
241         if (target) {
242             Box box = target->getBoundingBox();
243             t = (box.min + box.max) * 0.5f;
244         }
245 
246         lookAtPos(target ? &t : NULL);
247     }
248 
249 
250     void lookAtPos(const vec3 *t = NULL) {
251         float speed = lookAtSpeed * Core::deltaTime;
252         quat rot;
253 
254         if (jointChest > -1) {
255             if (t && aim(*t, jointChest, rangeChest, rot)) {
256                 if (fullChestRotation)
257                     rotChest = rotChest.slerp(rot, speed);
258                 else
259                     rotChest = rotChest.slerp(quat(0, 0, 0, 1).slerp(rot, 0.5f), speed);
260             } else
261                 rotChest = rotChest.slerp(quat(0, 0, 0, 1), speed);
262             animation.overrides[jointChest] = rotChest * animation.overrides[jointChest];
263         }
264 
265         if (jointHead > -1) {
266             if (t && aim(*t, jointHead, rangeHead, rot))
267                 rotHead = rotHead.slerp(rot, speed);
268             else
269                 rotHead = rotHead.slerp(quat(0, 0, 0, 1), speed);
270             animation.overrides[jointHead] = rotHead * animation.overrides[jointHead];
271         }
272     }
273 
addSparksCharacter274     void addSparks(uint32 mask) {
275         Sphere spheres[MAX_JOINTS];
276         int count = getSpheres(spheres);
277         for (int i = 0; i < count; i++)
278             if (mask & (1 << i)) {
279                 vec3 sprPos = spheres[i].center + (vec3(randf(), randf(), randf()) * 2.0f - 1.0f) * spheres[i].radius;
280                 game->addEntity(TR::Entity::SPARKLES, getRoomIndex(), sprPos);
281             }
282     }
283 
addBloodCharacter284     void addBlood(const vec3 &sprPos, const vec3 &sprVel) {
285         Sprite *sprite = (Sprite*)game->addEntity(TR::Entity::BLOOD, getRoomIndex(), sprPos, 0);
286         if (sprite)
287             sprite->velocity = sprVel;
288     }
289 
addBloodCharacter290     void addBlood(float radius, float height, const vec3 &sprVel) {
291         vec3 p = pos + vec3((randf() * 2.0f - 1.0f) * radius, -randf() * height, (randf() * 2.0f - 1.0f) * radius);
292         addBlood(p, sprVel);
293     }
294 
addBloodSpikesCharacter295     void addBloodSpikes() {
296         float ang = randf() * PI * 2.0f;
297         addBlood(64.0f,  512.0f, vec3(sinf(ang), 0.0f, cosf(ang)) * 20.0f);
298     }
299 
addBloodBladeCharacter300     void addBloodBlade() {
301         float ang = angle.y + (randf() - 0.5f) * 30.0f * DEG2RAD;
302         addBlood(64.0f, 744.0f, vec3(sinf(ang), 0.0f, cosf(ang)) * speed);
303     }
304 
addBloodSlamCharacter305     void addBloodSlam(Controller *trapSlam) {
306         for (int i = 0; i < 6; i++)
307             addBloodSpikes();
308     }
309 
bakeEnvironmentCharacter310     void bakeEnvironment(Texture *&environment) {
311         Core::beginFrame();
312 
313         flags.invisible = true;
314         if (!environment) {
315             uint32 opt = OPT_CUBEMAP | OPT_TARGET;
316             #ifdef USE_CUBEMAP_MIPS
317                 opt |= OPT_MIPMAPS;
318             #endif
319             environment = new Texture(256, 256, 1, FMT_RGB16, opt);
320         }
321         game->renderEnvironment(getRoomIndex(), pos - vec3(0.0f, 384.0f, 0.0f), &environment);
322         #ifdef USE_CUBEMAP_MIPS
323             environment->generateMipMap();
324         #endif
325         flags.invisible = false;
326 
327         Core::endFrame();
328     }
329 };
330 
331 #endif