2016-11-20 23:27:42 +03:00
|
|
|
#ifndef H_CHARACTER
|
|
|
|
#define H_CHARACTER
|
|
|
|
|
|
|
|
#include "controller.h"
|
2017-02-08 03:40:38 +03:00
|
|
|
#include "trigger.h"
|
2016-11-20 23:27:42 +03:00
|
|
|
|
|
|
|
struct Character : Controller {
|
2017-06-22 04:03:08 +03:00
|
|
|
float health;
|
2016-11-20 23:27:42 +03:00
|
|
|
float tilt;
|
|
|
|
quat rotHead, rotChest;
|
|
|
|
|
|
|
|
enum Stand {
|
|
|
|
STAND_AIR, STAND_GROUND, STAND_SLIDE, STAND_HANG, STAND_UNDERWATER, STAND_ONWATER
|
|
|
|
} stand;
|
2016-11-23 01:38:00 +03:00
|
|
|
int input, lastInput;
|
|
|
|
|
|
|
|
enum Key {
|
|
|
|
LEFT = 1 << 1,
|
|
|
|
RIGHT = 1 << 2,
|
|
|
|
FORTH = 1 << 3,
|
|
|
|
BACK = 1 << 4,
|
|
|
|
JUMP = 1 << 5,
|
|
|
|
WALK = 1 << 6,
|
|
|
|
ACTION = 1 << 7,
|
|
|
|
WEAPON = 1 << 8,
|
|
|
|
DEATH = 1 << 9
|
|
|
|
};
|
2016-11-20 23:27:42 +03:00
|
|
|
|
|
|
|
vec3 velocity;
|
|
|
|
float angleExt;
|
2017-02-15 04:11:26 +03:00
|
|
|
float speed;
|
|
|
|
|
2017-05-01 00:16:53 +03:00
|
|
|
int zone;
|
|
|
|
int box;
|
|
|
|
|
|
|
|
bool flying;
|
|
|
|
|
2017-02-15 04:11:26 +03:00
|
|
|
Collision collision;
|
2016-11-20 23:27:42 +03:00
|
|
|
|
2017-06-22 04:03:08 +03:00
|
|
|
Character(IGame *game, int entity, float health) : Controller(game, entity), health(health), tilt(0.0f), stand(STAND_GROUND), lastInput(0), velocity(0.0f), angleExt(0.0f) {
|
2016-11-20 23:27:42 +03:00
|
|
|
animation.initOverrides();
|
|
|
|
rotHead = rotChest = quat(0, 0, 0, 1);
|
2017-05-01 00:16:53 +03:00
|
|
|
|
|
|
|
flying = getEntity().type == TR::Entity::ENEMY_BAT;
|
|
|
|
updateZone();
|
|
|
|
}
|
|
|
|
|
2017-05-10 01:39:28 +03:00
|
|
|
bool updateZone() {
|
2017-05-01 00:16:53 +03:00
|
|
|
int dx, dz;
|
2017-05-10 01:39:28 +03:00
|
|
|
TR::Room::Sector &s = level->getSector(getRoomIndex(), int(pos.x), int(pos.z), dx, dz);
|
|
|
|
if (s.boxIndex == 0xFFFF)
|
|
|
|
return false;
|
|
|
|
box = s.boxIndex;
|
|
|
|
zone = getZones()[box];
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16* getZones() {
|
|
|
|
return flying ? level->zones[0].fly : level->zones[0].ground1;
|
2016-11-20 23:27:42 +03:00
|
|
|
}
|
|
|
|
|
2017-02-15 04:11:26 +03:00
|
|
|
void rotateY(float delta) {
|
|
|
|
angle.y += delta;
|
|
|
|
velocity = velocity.rotateY(-delta);
|
|
|
|
}
|
|
|
|
|
|
|
|
void rotateX(float delta) {
|
|
|
|
angle.x = clamp(angle.x + delta, -PI * 0.49f, PI * 0.49f);
|
|
|
|
}
|
|
|
|
|
2017-06-22 04:03:08 +03:00
|
|
|
virtual void hit(float damage, Controller *enemy = NULL) {
|
|
|
|
health = max(0.0f, health - damage);
|
2017-08-24 05:30:38 +03:00
|
|
|
}
|
2016-11-20 23:27:42 +03:00
|
|
|
|
|
|
|
virtual void checkRoom() {
|
|
|
|
TR::Level::FloorInfo info;
|
|
|
|
TR::Entity &e = getEntity();
|
2016-11-25 04:28:41 +03:00
|
|
|
level->getFloorInfo(e.room, e.x, e.y, e.z, info);
|
2016-11-20 23:27:42 +03:00
|
|
|
|
2017-02-15 04:11:26 +03:00
|
|
|
if (info.roomNext != TR::NO_ROOM)
|
2016-11-20 23:27:42 +03:00
|
|
|
e.room = info.roomNext;
|
|
|
|
|
2017-02-15 04:11:26 +03:00
|
|
|
if (info.roomBelow != TR::NO_ROOM && e.y > info.roomFloor)
|
2016-11-25 04:28:41 +03:00
|
|
|
e.room = info.roomBelow;
|
2016-11-20 23:27:42 +03:00
|
|
|
|
2017-02-15 04:11:26 +03:00
|
|
|
if (info.roomAbove != TR::NO_ROOM && e.y <= info.roomCeiling) {
|
2016-11-20 23:27:42 +03:00
|
|
|
if (stand == STAND_UNDERWATER && !level->rooms[info.roomAbove].flags.water) {
|
|
|
|
stand = STAND_ONWATER;
|
|
|
|
velocity.y = 0;
|
2016-11-25 04:28:41 +03:00
|
|
|
pos.y = float(info.roomCeiling);
|
2016-11-20 23:27:42 +03:00
|
|
|
updateEntity();
|
|
|
|
} else
|
|
|
|
if (stand != STAND_ONWATER)
|
|
|
|
e.room = info.roomAbove;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-23 01:38:00 +03:00
|
|
|
virtual void updateVelocity() {}
|
2016-11-20 23:27:42 +03:00
|
|
|
virtual void updatePosition() {}
|
|
|
|
virtual Stand getStand() { return stand; }
|
|
|
|
virtual int getHeight() { return 0; }
|
|
|
|
virtual int getStateAir() { return state; }
|
|
|
|
virtual int getStateGround() { return state; }
|
|
|
|
virtual int getStateSlide() { return state; }
|
|
|
|
virtual int getStateHang() { return state; }
|
|
|
|
virtual int getStateUnderwater() { return state; }
|
|
|
|
virtual int getStateOnwater() { return state; }
|
|
|
|
virtual int getStateDeath() { return state; }
|
|
|
|
virtual int getStateDefault() { return state; }
|
2016-11-23 01:38:00 +03:00
|
|
|
virtual int getInput() { return health <= 0 ? DEATH : 0; }
|
2016-11-20 23:27:42 +03:00
|
|
|
|
|
|
|
virtual void updateState() {
|
|
|
|
int state = animation.state;
|
|
|
|
|
|
|
|
if (input & DEATH)
|
|
|
|
state = getStateDeath();
|
|
|
|
else if (stand == STAND_GROUND)
|
|
|
|
state = getStateGround();
|
|
|
|
else if (stand == STAND_SLIDE)
|
|
|
|
state = getStateSlide();
|
|
|
|
else if (stand == STAND_HANG)
|
|
|
|
state = getStateHang();
|
|
|
|
else if (stand == STAND_AIR)
|
|
|
|
state = getStateAir();
|
|
|
|
else if (stand == STAND_UNDERWATER)
|
|
|
|
state = getStateUnderwater();
|
|
|
|
else
|
|
|
|
state = getStateOnwater();
|
|
|
|
|
|
|
|
// try to set new state
|
|
|
|
if (!animation.setState(state))
|
|
|
|
animation.setState(getStateDefault());
|
|
|
|
}
|
|
|
|
|
2017-05-10 01:39:28 +03:00
|
|
|
virtual void updateTilt(float value, float tiltSpeed, float tiltMax) {
|
|
|
|
value = clamp(value, -tiltMax, +tiltMax);
|
|
|
|
decrease(value - angle.z, angle.z, tiltSpeed);
|
|
|
|
}
|
|
|
|
|
2016-11-20 23:27:42 +03:00
|
|
|
virtual void updateTilt(bool active, float tiltSpeed, float tiltMax) {
|
|
|
|
// calculate turning tilt
|
|
|
|
if (active && (input & (LEFT | RIGHT)) && (tilt == 0.0f || (tilt < 0.0f && (input & LEFT)) || (tilt > 0.0f && (input & RIGHT)))) {
|
2017-02-15 04:11:26 +03:00
|
|
|
if (input & LEFT) tilt -= tiltSpeed;
|
|
|
|
if (input & RIGHT) tilt += tiltSpeed;
|
|
|
|
tilt = clamp(tilt, -tiltMax, +tiltMax);
|
|
|
|
} else {
|
|
|
|
if (tilt > 0.0f) tilt = max(0.0f, tilt - tiltSpeed);
|
|
|
|
if (tilt < 0.0f) tilt = min(0.0f, tilt + tiltSpeed);
|
|
|
|
}
|
2016-11-20 23:27:42 +03:00
|
|
|
angle.z = tilt;
|
|
|
|
}
|
|
|
|
|
2016-11-23 01:38:00 +03:00
|
|
|
bool isPressed(Key key) {
|
|
|
|
return (input & key) && !(lastInput & key);
|
|
|
|
}
|
|
|
|
|
2016-11-20 23:27:42 +03:00
|
|
|
virtual void update() {
|
2017-04-23 00:12:30 +03:00
|
|
|
vec3 p = pos;
|
2016-11-23 01:38:00 +03:00
|
|
|
lastInput = input;
|
2016-11-20 23:27:42 +03:00
|
|
|
input = getInput();
|
|
|
|
stand = getStand();
|
|
|
|
updateState();
|
|
|
|
Controller::update();
|
2017-05-10 01:39:28 +03:00
|
|
|
|
|
|
|
if (getEntity().flags.active) {
|
|
|
|
updateVelocity();
|
|
|
|
updatePosition();
|
|
|
|
if (p != pos) {
|
|
|
|
if (updateZone())
|
|
|
|
updateLights();
|
|
|
|
else
|
|
|
|
pos = p;
|
|
|
|
}
|
2017-05-01 00:16:53 +03:00
|
|
|
}
|
2016-11-20 23:27:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual void cmdJump(const vec3 &vel) {
|
|
|
|
velocity.x = sinf(angleExt) * vel.z;
|
|
|
|
velocity.y = vel.y;
|
|
|
|
velocity.z = cosf(angleExt) * vel.z;
|
|
|
|
stand = STAND_AIR;
|
|
|
|
}
|
2017-02-08 03:40:38 +03:00
|
|
|
|
|
|
|
virtual void doBubbles() {
|
|
|
|
int count = rand() % 3;
|
|
|
|
if (!count) return;
|
|
|
|
playSound(TR::SND_BUBBLE, pos, Sound::Flags::PAN);
|
|
|
|
vec3 head = animation.getJoints(getMatrix(), 14, true) * vec3(0.0f, 0.0f, 50.0f);
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
int index = Sprite::add(game, TR::Entity::BUBBLE, getRoomIndex(), int(head.x), int(head.y), int(head.z), Sprite::FRAME_RANDOM, true);
|
|
|
|
if (index > -1)
|
|
|
|
level->entities[index].controller = new Bubble(game, index);
|
|
|
|
}
|
|
|
|
}
|
2016-11-20 23:27:42 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|