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