mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-17 18:36:43 +02:00
This commit is contained in:
@@ -798,7 +798,7 @@ struct ZoneCache {
|
|||||||
return items = new Item(zone, count, zones, boxes, items);
|
return items = new Item(zone, count, zones, boxes, items);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 findPath(int ascend, int descend, int boxStart, int boxEnd, uint16 *zones, uint16 **boxes) {
|
uint16 findPath(int ascend, int descend, bool big, int boxStart, int boxEnd, uint16 *zones, uint16 **boxes) {
|
||||||
if (boxStart == 0xFFFF || boxEnd == 0xFFFF)
|
if (boxStart == 0xFFFF || boxEnd == 0xFFFF)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -859,6 +859,9 @@ struct ZoneCache {
|
|||||||
// has same zone
|
// has same zone
|
||||||
if (zones[index] != zone)
|
if (zones[index] != zone)
|
||||||
continue;
|
continue;
|
||||||
|
// check passability
|
||||||
|
if (big && level->boxes[index].overlap.blockable)
|
||||||
|
continue;
|
||||||
// check blocking (doors)
|
// check blocking (doors)
|
||||||
if (level->boxes[index].overlap.block)
|
if (level->boxes[index].overlap.block)
|
||||||
continue;
|
continue;
|
||||||
|
12
src/camera.h
12
src/camera.h
@@ -28,6 +28,8 @@ struct Camera : Controller {
|
|||||||
bool firstPerson;
|
bool firstPerson;
|
||||||
bool isVR;
|
bool isVR;
|
||||||
|
|
||||||
|
float shake;
|
||||||
|
|
||||||
Camera(IGame *game, Lara *owner) : Controller(game, owner ? owner->entity : 0), owner(owner), frustum(new Frustum()), timer(0.0f), actTargetEntity(-1), actCamera(-1), reflectPlane(NULL), isVR(false) {
|
Camera(IGame *game, Lara *owner) : Controller(game, owner ? owner->entity : 0), owner(owner), frustum(new Frustum()), timer(0.0f), actTargetEntity(-1), actCamera(-1), reflectPlane(NULL), isVR(false) {
|
||||||
changeView(false);
|
changeView(false);
|
||||||
cutscene = owner->getEntity().type != TR::Entity::LARA && level->cameraFrames;
|
cutscene = owner->getEntity().type != TR::Entity::LARA && level->cameraFrames;
|
||||||
@@ -90,6 +92,9 @@ struct Camera : Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void update() {
|
virtual void update() {
|
||||||
|
if (shake > 0.0f)
|
||||||
|
shake = max(0.0f, shake - Core::deltaTime);
|
||||||
|
|
||||||
#ifndef LEVEL_EDITOR
|
#ifndef LEVEL_EDITOR
|
||||||
if (cutscene) { // cutscene
|
if (cutscene) { // cutscene
|
||||||
timer += Core::deltaTime * 30;
|
timer += Core::deltaTime * 30;
|
||||||
@@ -149,8 +154,8 @@ struct Camera : Controller {
|
|||||||
advAngle.y = lerp(clampAngle(advAngle.y), 0.0f, t);
|
advAngle.y = lerp(clampAngle(advAngle.y), 0.0f, t);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (owner->health > 0)
|
||||||
angle = owner->angle + advAngle;
|
angle = owner->angle + advAngle;
|
||||||
angle.z = 0.0f;
|
angle.z = 0.0f;
|
||||||
|
|
||||||
if (owner->stand == Lara::STAND_ONWATER)
|
if (owner->stand == Lara::STAND_ONWATER)
|
||||||
@@ -275,13 +280,14 @@ struct Camera : Controller {
|
|||||||
Core::mViewInv = mViewInv;
|
Core::mViewInv = mViewInv;
|
||||||
|
|
||||||
Core::mView = Core::mViewInv.inverse();
|
Core::mView = Core::mViewInv.inverse();
|
||||||
|
if (shake > 0.0f)
|
||||||
|
Core::mView.translate(vec3(0.0f, sinf(shake * PI * 7) * shake * 48.0f, 0.0f));
|
||||||
|
|
||||||
if (isVR)
|
if (isVR)
|
||||||
Core::mView.translate(Core::mViewInv.right.xyz * (-Core::eye * 32.0f));
|
Core::mView.translate(Core::mViewInv.right.xyz * (-Core::eye * 32.0f));
|
||||||
|
|
||||||
Core::mProj = getProjMatrix();
|
Core::mProj = getProjMatrix();
|
||||||
|
|
||||||
// TODO: camera shake
|
|
||||||
// TODO: temporal anti-aliasing
|
// TODO: temporal anti-aliasing
|
||||||
// Core::mProj.e02 = (randf() - 0.5f) * 32.0f / Core::width;
|
// Core::mProj.e02 = (randf() - 0.5f) * 32.0f / Core::width;
|
||||||
// Core::mProj.e12 = (randf() - 0.5f) * 32.0f / Core::height;
|
// Core::mProj.e12 = (randf() - 0.5f) * 32.0f / Core::height;
|
||||||
|
@@ -20,7 +20,7 @@ struct IGame {
|
|||||||
virtual MeshBuilder* getMesh() { return NULL; }
|
virtual MeshBuilder* getMesh() { return NULL; }
|
||||||
virtual Controller* getCamera() { return NULL; }
|
virtual Controller* getCamera() { return NULL; }
|
||||||
virtual uint16 getRandomBox(uint16 zone, uint16 *zones) { return 0; }
|
virtual uint16 getRandomBox(uint16 zone, uint16 *zones) { return 0; }
|
||||||
virtual uint16 findPath(int ascend, int descend, int boxStart, int boxEnd, uint16 *zones, uint16 **boxes) { return 0; }
|
virtual uint16 findPath(int ascend, int descend, bool big, int boxStart, int boxEnd, uint16 *zones, uint16 **boxes) { return 0; }
|
||||||
virtual void setClipParams(float clipSign, float clipHeight) {}
|
virtual void setClipParams(float clipSign, float clipHeight) {}
|
||||||
virtual void setWaterParams(float height) {}
|
virtual void setWaterParams(float height) {}
|
||||||
virtual void updateParams() {}
|
virtual void updateParams() {}
|
||||||
@@ -28,6 +28,7 @@ struct IGame {
|
|||||||
virtual void setShader(Core::Pass pass, Shader::Type type, bool underwater = false, bool alphaTest = false) {}
|
virtual void setShader(Core::Pass pass, Shader::Type type, bool underwater = false, bool alphaTest = false) {}
|
||||||
virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0) {}
|
virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0) {}
|
||||||
virtual void renderCompose(int roomIndex, bool genShadowMask = false) {}
|
virtual void renderCompose(int roomIndex, bool genShadowMask = false) {}
|
||||||
|
virtual void fxQuake(float time) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Controller {
|
struct Controller {
|
||||||
@@ -265,14 +266,14 @@ struct Controller {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool collide(Controller *controller) {
|
int collide(Controller *controller, bool checkBoxes = true) {
|
||||||
TR::Model *a = getModel();
|
TR::Model *a = getModel();
|
||||||
TR::Model *b = getModel();
|
TR::Model *b = controller->getModel();
|
||||||
if (!a || !b)
|
if (!a || !b)
|
||||||
return false;
|
return 0;
|
||||||
|
|
||||||
if (!getBoundingBox().intersect(controller->getBoundingBox()))
|
if (checkBoxes && !getBoundingBox().intersect(controller->getBoundingBox()))
|
||||||
return false;
|
return 0;
|
||||||
|
|
||||||
ASSERT(a->mCount <= 34);
|
ASSERT(a->mCount <= 34);
|
||||||
ASSERT(b->mCount <= 34);
|
ASSERT(b->mCount <= 34);
|
||||||
@@ -283,12 +284,15 @@ struct Controller {
|
|||||||
getSpheres(aSpheres);
|
getSpheres(aSpheres);
|
||||||
controller->getSpheres(bSpheres);
|
controller->getSpheres(bSpheres);
|
||||||
|
|
||||||
|
int mask = 0;
|
||||||
for (int i = 0; i < a->mCount; i++)
|
for (int i = 0; i < a->mCount; i++)
|
||||||
for (int j = 0; j < b->mCount; j++)
|
if (aSpheres[i].radius > 0.0f)
|
||||||
if (aSpheres[i].intersect(bSpheres[j]))
|
for (int j = 0; j < b->mCount; j++)
|
||||||
return true;
|
if (bSpheres[j].radius > 0.0f && bSpheres[j].intersect(aSpheres[i])) {
|
||||||
|
mask |= (1 << i);
|
||||||
return false;
|
break;
|
||||||
|
}
|
||||||
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 trace(int fromRoom, const vec3 &from, const vec3 &to, int &room, bool isCamera) { // TODO: use Bresenham
|
vec3 trace(int fromRoom, const vec3 &from, const vec3 &to, int &room, bool isCamera) { // TODO: use Bresenham
|
||||||
@@ -452,6 +456,7 @@ struct Controller {
|
|||||||
if (cmd == TR::ANIM_CMD_EFFECT) {
|
if (cmd == TR::ANIM_CMD_EFFECT) {
|
||||||
switch (fx) {
|
switch (fx) {
|
||||||
case TR::EFFECT_ROTATE_180 : angle.y = angle.y + PI; break;
|
case TR::EFFECT_ROTATE_180 : angle.y = angle.y + PI; break;
|
||||||
|
case TR::EFFECT_FLOOR_SHAKE : game->fxQuake(0.5f * max(0.0f, 1.0f - (pos - ((Controller*)level->cameraController)->pos).length2() / (15 * 1024 * 15 * 1024) )); break;
|
||||||
case TR::EFFECT_LARA_BUBBLES : doBubbles(); break;
|
case TR::EFFECT_LARA_BUBBLES : doBubbles(); break;
|
||||||
default : cmdEffect(fx); break;
|
default : cmdEffect(fx); break;
|
||||||
}
|
}
|
||||||
|
19
src/debug.h
19
src/debug.h
@@ -496,16 +496,29 @@ namespace Debug {
|
|||||||
TR::Model *m = controller->getModel();
|
TR::Model *m = controller->getModel();
|
||||||
if (!m) continue;
|
if (!m) continue;
|
||||||
|
|
||||||
Box box = controller->getBoundingBoxLocal();
|
bool bboxIntersect = false;
|
||||||
Debug::Draw::box(matrix, box.min, box.max, vec4(1.0));
|
|
||||||
|
|
||||||
Sphere spheres[34];
|
Sphere spheres[34];
|
||||||
ASSERT(m->mCount <= 34);
|
ASSERT(m->mCount <= 34);
|
||||||
controller->getSpheres(spheres);
|
controller->getSpheres(spheres);
|
||||||
|
int mask = 0;
|
||||||
|
for (int j = 0; j < level.entitiesCount; j++) {
|
||||||
|
TR::Entity &t = level.entities[j];
|
||||||
|
if (j == i || ((!t.isEnemy() || !t.flags.active) && t.type != TR::Entity::LARA)) continue;
|
||||||
|
Controller *enemy = (Controller*)t.controller;
|
||||||
|
if (!controller->getBoundingBox().intersect(enemy->getBoundingBox()))
|
||||||
|
continue;
|
||||||
|
bboxIntersect = true;
|
||||||
|
mask |= controller->collide(enemy);
|
||||||
|
}
|
||||||
|
|
||||||
|
Box box = controller->getBoundingBoxLocal();
|
||||||
|
Debug::Draw::box(matrix, box.min, box.max, bboxIntersect ? vec4(1, 0, 0, 1): vec4(1));
|
||||||
|
|
||||||
|
|
||||||
for (int joint = 0; joint < m->mCount; joint++) {
|
for (int joint = 0; joint < m->mCount; joint++) {
|
||||||
Sphere &sphere = spheres[joint];
|
Sphere &sphere = spheres[joint];
|
||||||
Debug::Draw::sphere(sphere.center, sphere.radius, vec4(0, 1, 1, 0.5f));
|
Debug::Draw::sphere(sphere.center, sphere.radius, (mask & (1 << joint)) ? vec4(1, 0, 0, 0.5f) : vec4(0, 1, 1, 0.5f));
|
||||||
/*
|
/*
|
||||||
{ //if (e.id != 0) {
|
{ //if (e.id != 0) {
|
||||||
char buf[255];
|
char buf[255];
|
||||||
|
384
src/enemy.h
384
src/enemy.h
@@ -54,6 +54,7 @@ struct Enemy : Character {
|
|||||||
vec3 waypoint;
|
vec3 waypoint;
|
||||||
|
|
||||||
float thinkTime;
|
float thinkTime;
|
||||||
|
float length; // dist from center to head (jaws)
|
||||||
float aggression;
|
float aggression;
|
||||||
int radius;
|
int radius;
|
||||||
int stepHeight;
|
int stepHeight;
|
||||||
@@ -65,11 +66,20 @@ struct Enemy : Character {
|
|||||||
int jointChest;
|
int jointChest;
|
||||||
int jointHead;
|
int jointHead;
|
||||||
|
|
||||||
Enemy(IGame *game, int entity, int health, int radius, float aggression) : Character(game, entity, health), ai(AI_RANDOM), mood(MOOD_SLEEP), wound(false), nextState(0), targetBox(-1), thinkTime(0.0f), aggression(aggression), radius(radius), target(NULL), path(NULL) {
|
float targetDist;
|
||||||
|
bool targetDead;
|
||||||
|
bool targetInView; // target in enemy view zone
|
||||||
|
bool targetFromView; // enemy in target view zone
|
||||||
|
bool targetCanAttack;
|
||||||
|
|
||||||
|
Enemy(IGame *game, int entity, int health, int radius, float length, float aggression) : Character(game, entity, health), ai(AI_RANDOM), mood(MOOD_SLEEP), wound(false), nextState(0), targetBox(-1), thinkTime(1.0f / 30.0f), length(length), aggression(aggression), radius(radius), target(NULL), path(NULL) {
|
||||||
stepHeight = 256;
|
stepHeight = 256;
|
||||||
dropHeight = -256;
|
dropHeight = -256;
|
||||||
|
|
||||||
jointChest = jointHead = -1;
|
jointChest = jointHead = -1;
|
||||||
|
|
||||||
|
targetDist = +INF;
|
||||||
|
targetInView = targetFromView = targetCanAttack = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~Enemy() {
|
virtual ~Enemy() {
|
||||||
@@ -105,11 +115,14 @@ struct Enemy : Character {
|
|||||||
if (a.contains(x, z))
|
if (a.contains(x, z))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
bool big = getEntity().isBigEnemy();
|
||||||
TR::Overlap *o = &level->overlaps[a.overlap.index];
|
TR::Overlap *o = &level->overlaps[a.overlap.index];
|
||||||
do {
|
do {
|
||||||
TR::Box &b = level->boxes[o->boxIndex];
|
TR::Box &b = level->boxes[o->boxIndex];
|
||||||
if (!b.contains(x, z))
|
if (!b.contains(x, z))
|
||||||
continue;
|
continue;
|
||||||
|
if (big && b.overlap.blockable)
|
||||||
|
continue;
|
||||||
if (getZones()[o->boxIndex] == zone) {
|
if (getZones()[o->boxIndex] == zone) {
|
||||||
int d = a.floor - b.floor;
|
int d = a.floor - b.floor;
|
||||||
if (d <= stepHeight && d >= dropHeight)
|
if (d <= stepHeight && d >= dropHeight)
|
||||||
@@ -181,12 +194,12 @@ struct Enemy : Character {
|
|||||||
animation.overrideMask &= ~(1 << chest);
|
animation.overrideMask &= ~(1 << chest);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lookAt(int target, int chest, int head) {
|
void lookAt(int target, int chest, int head, bool rotate = false) {
|
||||||
float speed = 8.0f * Core::deltaTime;
|
float speed = 8.0f * Core::deltaTime;
|
||||||
quat rot;
|
quat rot;
|
||||||
|
|
||||||
if (chest > -1) {
|
if (chest > -1) {
|
||||||
if (aim(target, chest, vec4(-PI * 0.8f, PI * 0.8f, -PI * 0.75f, PI * 0.75f), rot))
|
if (rotate && aim(target, chest, vec4(-PI * 0.8f, PI * 0.8f, -PI * 0.75f, PI * 0.75f), rot))
|
||||||
rotChest = rotChest.slerp(quat(0, 0, 0, 1).slerp(rot, 0.5f), speed);
|
rotChest = rotChest.slerp(quat(0, 0, 0, 1).slerp(rot, 0.5f), speed);
|
||||||
else
|
else
|
||||||
rotChest = rotChest.slerp(quat(0, 0, 0, 1), speed);
|
rotChest = rotChest.slerp(quat(0, 0, 0, 1), speed);
|
||||||
@@ -194,7 +207,7 @@ struct Enemy : Character {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (head > -1) {
|
if (head > -1) {
|
||||||
if (aim(target, head, vec4(-PI * 0.25f, PI * 0.25f, -PI * 0.5f, PI * 0.5f), rot))
|
if (rotate && aim(target, head, vec4(-PI * 0.25f, PI * 0.25f, -PI * 0.5f, PI * 0.5f), rot))
|
||||||
rotHead = rotHead.slerp(rot, speed);
|
rotHead = rotHead.slerp(rot, speed);
|
||||||
else
|
else
|
||||||
rotHead = rotHead.slerp(quat(0, 0, 0, 1), speed);
|
rotHead = rotHead.slerp(quat(0, 0, 0, 1), speed);
|
||||||
@@ -244,7 +257,6 @@ struct Enemy : Character {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
virtual void hit(int damage, Controller *enemy = NULL) {
|
virtual void hit(int damage, Controller *enemy = NULL) {
|
||||||
Character::hit(damage, enemy);
|
Character::hit(damage, enemy);
|
||||||
wound = true;
|
wound = true;
|
||||||
@@ -301,9 +313,18 @@ struct Enemy : Character {
|
|||||||
|
|
||||||
if (!target) {
|
if (!target) {
|
||||||
mood = MOOD_SLEEP;
|
mood = MOOD_SLEEP;
|
||||||
|
targetDist = +INF;
|
||||||
|
targetInView = targetFromView = targetCanAttack = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec3 targetVec = target->pos - pos - getDir() * length;
|
||||||
|
targetDist = targetVec.length();
|
||||||
|
targetDead = target->health <= 0;
|
||||||
|
targetInView = targetVec.dot(getDir()) > 0;
|
||||||
|
targetFromView = targetVec.dot(target->getDir()) < 0;
|
||||||
|
targetCanAttack = targetInView && fabsf(targetVec.y) <= 256.0f;
|
||||||
|
|
||||||
int targetBoxOld = targetBox;
|
int targetBoxOld = targetBox;
|
||||||
|
|
||||||
// update mood
|
// update mood
|
||||||
@@ -364,7 +385,7 @@ struct Enemy : Character {
|
|||||||
targetBoxOld = -1;
|
targetBoxOld = -1;
|
||||||
|
|
||||||
if (targetBoxOld != targetBox) {
|
if (targetBoxOld != targetBox) {
|
||||||
if (findPath(stepHeight, dropHeight))
|
if (findPath(stepHeight, dropHeight, getEntity().isBigEnemy()))
|
||||||
nextWaypoint();
|
nextWaypoint();
|
||||||
else
|
else
|
||||||
targetBox = -1;
|
targetBox = -1;
|
||||||
@@ -449,12 +470,12 @@ struct Enemy : Character {
|
|||||||
return !((e.x > t.x) ^ (x > 0)) || !((e.z > t.z) ^ (z > 0));
|
return !((e.x > t.x) ^ (x > 0)) || !((e.z > t.z) ^ (z > 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool findPath(int ascend, int descend) {
|
bool findPath(int ascend, int descend, bool big) {
|
||||||
delete path;
|
delete path;
|
||||||
path = NULL;
|
path = NULL;
|
||||||
|
|
||||||
uint16 *boxes;
|
uint16 *boxes;
|
||||||
uint16 count = game->findPath(ascend, descend, box, targetBox, getZones(), &boxes);
|
uint16 count = game->findPath(ascend, descend, big, box, targetBox, getZones(), &boxes);
|
||||||
if (count) {
|
if (count) {
|
||||||
path = new Path(level, boxes, count);
|
path = new Path(level, boxes, count);
|
||||||
return true;
|
return true;
|
||||||
@@ -474,6 +495,10 @@ struct Enemy : Character {
|
|||||||
|
|
||||||
struct Wolf : Enemy {
|
struct Wolf : Enemy {
|
||||||
|
|
||||||
|
enum {
|
||||||
|
HIT_MASK = 0x774F, // body, head, front legs
|
||||||
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
ANIM_DEATH = 20,
|
ANIM_DEATH = 20,
|
||||||
ANIM_DEATH_RUN = 21,
|
ANIM_DEATH_RUN = 21,
|
||||||
@@ -496,7 +521,7 @@ struct Wolf : Enemy {
|
|||||||
STATE_BITE ,
|
STATE_BITE ,
|
||||||
};
|
};
|
||||||
|
|
||||||
Wolf(IGame *game, int entity) : Enemy(game, entity, 6, 341, 0.25f) {
|
Wolf(IGame *game, int entity) : Enemy(game, entity, 6, 341, 375.0f, 0.25f) {
|
||||||
dropHeight = -1024;
|
dropHeight = -1024;
|
||||||
jointChest = 2;
|
jointChest = 2;
|
||||||
jointHead = 3;
|
jointHead = 3;
|
||||||
@@ -511,11 +536,9 @@ struct Wolf : Enemy {
|
|||||||
if (!think(false))
|
if (!think(false))
|
||||||
return state;
|
return state;
|
||||||
|
|
||||||
float angle, dist;
|
float angle;
|
||||||
getTargetInfo(0, NULL, NULL, &angle, NULL);
|
getTargetInfo(0, NULL, NULL, &angle, NULL);
|
||||||
|
|
||||||
dist = (target && target->health > 0) ? (pos - target->pos).length() : +INF;
|
|
||||||
|
|
||||||
bool inZone = target ? target->zone == zone : false;
|
bool inZone = target ? target->zone == zone : false;
|
||||||
|
|
||||||
if (nextState == state)
|
if (nextState == state)
|
||||||
@@ -546,15 +569,18 @@ struct Wolf : Enemy {
|
|||||||
case STATE_GROWL :
|
case STATE_GROWL :
|
||||||
if (nextState != STATE_NONE) return nextState;
|
if (nextState != STATE_NONE) return nextState;
|
||||||
if (mood == MOOD_ESCAPE) return STATE_RUN;
|
if (mood == MOOD_ESCAPE) return STATE_RUN;
|
||||||
if (dist < WOLF_DIST_BITE) return STATE_BITE;
|
if (targetDist < WOLF_DIST_BITE && targetCanAttack) return STATE_BITE;
|
||||||
if (mood == MOOD_STALK) return STATE_STALK;
|
if (mood == MOOD_STALK) return STATE_STALK;
|
||||||
if (mood == MOOD_SLEEP) return STATE_STOP;
|
if (mood == MOOD_SLEEP) return STATE_STOP;
|
||||||
return STATE_RUN;
|
return STATE_RUN;
|
||||||
case STATE_STALK :
|
case STATE_STALK :
|
||||||
if (mood == MOOD_ESCAPE) return STATE_RUN;
|
if (mood == MOOD_ESCAPE) return STATE_RUN;
|
||||||
if (dist < WOLF_DIST_BITE) return STATE_BITE;
|
if (targetDist < WOLF_DIST_BITE && targetCanAttack) return STATE_BITE;
|
||||||
if (dist > WOLF_DIST_STALK) return STATE_RUN;
|
if (targetDist > WOLF_DIST_STALK) return STATE_RUN;
|
||||||
if (mood == MOOD_ATTACK) return STATE_RUN;
|
if (mood == MOOD_ATTACK) {
|
||||||
|
if (!targetInView || targetFromView || targetDist > WOLF_DIST_ATTACK)
|
||||||
|
return STATE_RUN;
|
||||||
|
}
|
||||||
if (randf() < 0.012f) {
|
if (randf() < 0.012f) {
|
||||||
nextState = STATE_HOWL;
|
nextState = STATE_HOWL;
|
||||||
return STATE_GROWL;
|
return STATE_GROWL;
|
||||||
@@ -562,15 +588,15 @@ struct Wolf : Enemy {
|
|||||||
if (mood == MOOD_SLEEP) return STATE_GROWL;
|
if (mood == MOOD_SLEEP) return STATE_GROWL;
|
||||||
break;
|
break;
|
||||||
case STATE_RUN :
|
case STATE_RUN :
|
||||||
if (dist < WOLF_DIST_ATTACK) {
|
if (targetDist < WOLF_DIST_ATTACK && targetInView) {
|
||||||
if (dist < WOLF_DIST_ATTACK * 0.5f && fabsf(angle) < PI * 0.5f) {
|
if (targetDist < WOLF_DIST_ATTACK * 0.5f && targetFromView) {
|
||||||
nextState = STATE_NONE;
|
nextState = STATE_NONE;
|
||||||
return STATE_ATTACK;
|
return STATE_ATTACK;
|
||||||
}
|
}
|
||||||
nextState = STATE_STALK;
|
nextState = STATE_STALK;
|
||||||
return STATE_GROWL;
|
return STATE_GROWL;
|
||||||
}
|
}
|
||||||
if (mood == MOOD_STALK && dist < WOLF_DIST_STALK) {
|
if (mood == MOOD_STALK && targetDist < WOLF_DIST_STALK) {
|
||||||
nextState = STATE_STALK;
|
nextState = STATE_STALK;
|
||||||
return STATE_GROWL;
|
return STATE_GROWL;
|
||||||
}
|
}
|
||||||
@@ -578,7 +604,7 @@ struct Wolf : Enemy {
|
|||||||
break;
|
break;
|
||||||
case STATE_ATTACK :
|
case STATE_ATTACK :
|
||||||
case STATE_BITE :
|
case STATE_BITE :
|
||||||
if (nextState == STATE_NONE && target->health > 0 && collide(target)) {
|
if (nextState == STATE_NONE && targetInView && (collide(target) & HIT_MASK)) {
|
||||||
bite(animation.getJoints(getMatrix(), jointHead, true).pos, state == STATE_ATTACK ? 50 : 100);
|
bite(animation.getJoints(getMatrix(), jointHead, true).pos, state == STATE_ATTACK ? 50 : 100);
|
||||||
nextState = state == STATE_ATTACK ? STATE_RUN : STATE_GROWL;
|
nextState = state == STATE_ATTACK ? STATE_RUN : STATE_GROWL;
|
||||||
}
|
}
|
||||||
@@ -599,6 +625,7 @@ struct Wolf : Enemy {
|
|||||||
|
|
||||||
virtual void updatePosition() {
|
virtual void updatePosition() {
|
||||||
float angleY = 0.0f;
|
float angleY = 0.0f;
|
||||||
|
|
||||||
if (state == STATE_RUN || state == STATE_WALK || state == STATE_STALK)
|
if (state == STATE_RUN || state == STATE_WALK || state == STATE_STALK)
|
||||||
getTargetInfo(0, NULL, NULL, &angleY, NULL);
|
getTargetInfo(0, NULL, NULL, &angleY, NULL);
|
||||||
|
|
||||||
@@ -610,7 +637,7 @@ struct Wolf : Enemy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Enemy::updatePosition();
|
Enemy::updatePosition();
|
||||||
setOverrides(state == STATE_RUN || state == STATE_WALK || state == STATE_STALK, jointChest, jointHead);
|
setOverrides(state != STATE_DEATH, jointChest, jointHead);
|
||||||
lookAt(target ? target->entity : -1, jointChest, jointHead);
|
lookAt(target ? target->entity : -1, jointChest, jointHead);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -625,6 +652,10 @@ struct Wolf : Enemy {
|
|||||||
|
|
||||||
struct Bear : Enemy {
|
struct Bear : Enemy {
|
||||||
|
|
||||||
|
enum {
|
||||||
|
HIT_MASK = 0x2406C, // front legs and head
|
||||||
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
ANIM_DEATH_HIND = 19,
|
ANIM_DEATH_HIND = 19,
|
||||||
ANIM_DEATH = 20,
|
ANIM_DEATH = 20,
|
||||||
@@ -644,7 +675,7 @@ struct Bear : Enemy {
|
|||||||
STATE_DEATH ,
|
STATE_DEATH ,
|
||||||
};
|
};
|
||||||
|
|
||||||
Bear(IGame *game, int entity) : Enemy(game, entity, 20, 341, 0.5f) {
|
Bear(IGame *game, int entity) : Enemy(game, entity, 20, 341, 500.0f, 0.5f) {
|
||||||
jointChest = 13;
|
jointChest = 13;
|
||||||
jointHead = 14;
|
jointHead = 14;
|
||||||
nextState = STATE_NONE;
|
nextState = STATE_NONE;
|
||||||
@@ -654,73 +685,74 @@ struct Bear : Enemy {
|
|||||||
if (!getEntity().flags.active)
|
if (!getEntity().flags.active)
|
||||||
return state;
|
return state;
|
||||||
|
|
||||||
if (!think(false))
|
if (!think(true))
|
||||||
return state;
|
return state;
|
||||||
|
|
||||||
if (nextState == state)
|
if (nextState == state)
|
||||||
nextState = STATE_NONE;
|
nextState = STATE_NONE;
|
||||||
|
|
||||||
float dist = target ? (pos - target->pos).length() : +INF;
|
|
||||||
|
|
||||||
bool targetDead = target->health <= 0;
|
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case STATE_WALK :
|
case STATE_WALK :
|
||||||
if (targetDead && collide(target))
|
if (nextState != STATE_NONE) return STATE_STOP;
|
||||||
return STATE_STOP; // eat lara! >:E
|
if (targetDead && targetInView && (collide(target) & HIT_MASK))
|
||||||
else
|
return nextState = STATE_STOP; // eat lara! >:E
|
||||||
if (mood != MOOD_SLEEP) {
|
if (mood != MOOD_SLEEP) {
|
||||||
if (mood == MOOD_ESCAPE)
|
if (mood == MOOD_ESCAPE)
|
||||||
nextState = STATE_NONE;
|
nextState = STATE_NONE;
|
||||||
return STATE_STOP;
|
return STATE_STOP;
|
||||||
} else if (randf() < 0.003f) {
|
} else if (randf() < 0.003f) {
|
||||||
nextState = STATE_GROWL;
|
nextState = STATE_GROWL;
|
||||||
return STATE_STOP;
|
return STATE_STOP;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case STATE_STOP :
|
case STATE_STOP :
|
||||||
if (targetDead)
|
if (targetDead)
|
||||||
return dist <= BEAR_DIST_EAT ? STATE_EAT : STATE_WALK;
|
return (targetDist < BEAR_DIST_EAT && targetCanAttack) ? STATE_EAT : STATE_WALK;
|
||||||
else
|
else
|
||||||
return nextState != STATE_NONE ? nextState : (mood == MOOD_SLEEP ? STATE_WALK : STATE_RUN);
|
return nextState != STATE_NONE ? nextState : (mood == MOOD_SLEEP ? STATE_WALK : STATE_RUN);
|
||||||
case STATE_HIND :
|
case STATE_HIND :
|
||||||
if (collide(target)) {
|
if (wound) {
|
||||||
return STATE_HOWL;
|
|
||||||
}
|
|
||||||
if (mood == MOOD_ESCAPE) {
|
|
||||||
nextState = STATE_NONE;
|
nextState = STATE_NONE;
|
||||||
return STATE_HOWL;
|
return STATE_HOWL;
|
||||||
}
|
}
|
||||||
if (mood == MOOD_SLEEP || randf() < 0.003f) {
|
|
||||||
|
if (targetInView && (collide(target) & HIT_MASK)) return STATE_HOWL;
|
||||||
|
|
||||||
|
if (mood == MOOD_ESCAPE)
|
||||||
|
nextState = STATE_NONE;
|
||||||
|
else if (mood == MOOD_SLEEP || randf() < 0.003f)
|
||||||
nextState = STATE_GROWL;
|
nextState = STATE_GROWL;
|
||||||
return STATE_HOWL;
|
else if (targetDist > BEAR_DIST_HOWL || randf() < 0.05f)
|
||||||
}
|
|
||||||
if (dist > BEAR_DIST_HOWL || randf() < 0.05f) {
|
|
||||||
nextState = STATE_STOP;
|
nextState = STATE_STOP;
|
||||||
return STATE_HOWL;
|
|
||||||
}
|
return STATE_HOWL;
|
||||||
break;
|
break;
|
||||||
case STATE_RUN :
|
case STATE_RUN :
|
||||||
if (collide(target))
|
if (collide(target) & HIT_MASK)
|
||||||
target->hit(3, this);
|
target->hit(3, this);
|
||||||
if (targetDead || mood == MOOD_SLEEP)
|
if (targetDead || mood == MOOD_SLEEP)
|
||||||
return STATE_STOP;
|
return STATE_STOP;
|
||||||
if (dist < BEAR_DIST_HOWL && randf() < 0.025f) {
|
if (nextState != STATE_NONE) return STATE_STOP;
|
||||||
nextState = STATE_HOWL;
|
if (targetInView) {
|
||||||
return STATE_STOP;
|
if (!wound && targetDist < BEAR_DIST_HOWL && randf() < 0.025f) {
|
||||||
} else if (dist < BEAR_DIST_BITE) {
|
nextState = STATE_HOWL;
|
||||||
nextState = STATE_NONE;
|
return STATE_STOP;
|
||||||
return STATE_BITE;
|
}
|
||||||
|
if (targetDist < BEAR_DIST_BITE) return STATE_BITE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case STATE_HOWL :
|
case STATE_HOWL :
|
||||||
|
if (wound) {
|
||||||
|
nextState = STATE_NONE;
|
||||||
|
return STATE_STOP;
|
||||||
|
}
|
||||||
if (nextState != STATE_NONE) return nextState;
|
if (nextState != STATE_NONE) return nextState;
|
||||||
if (mood == MOOD_SLEEP || mood == MOOD_ESCAPE) return STATE_STOP;
|
if (mood == MOOD_SLEEP || mood == MOOD_ESCAPE) return STATE_STOP;
|
||||||
if (dist < BEAR_DIST_ATTACK) return STATE_ATTACK;
|
if (targetDist < BEAR_DIST_ATTACK) return STATE_ATTACK;
|
||||||
return STATE_HIND;
|
return STATE_HIND;
|
||||||
case STATE_BITE :
|
case STATE_BITE :
|
||||||
case STATE_ATTACK :
|
case STATE_ATTACK :
|
||||||
if (nextState == STATE_NONE && collide(target)) {
|
if (nextState == STATE_NONE && (collide(target) & HIT_MASK)) {
|
||||||
bite(animation.getJoints(getMatrix(), jointHead, true).pos, state == STATE_BITE ? 200 : 400);
|
bite(animation.getJoints(getMatrix(), jointHead, true).pos, state == STATE_BITE ? 200 : 400);
|
||||||
nextState = state == STATE_BITE ? STATE_STOP : STATE_HOWL;
|
nextState = state == STATE_BITE ? STATE_STOP : STATE_HOWL;
|
||||||
}
|
}
|
||||||
@@ -778,7 +810,7 @@ struct Bat : Enemy {
|
|||||||
STATE_DEATH,
|
STATE_DEATH,
|
||||||
};
|
};
|
||||||
|
|
||||||
Bat(IGame *game, int entity) : Enemy(game, entity, 1, 102, 0.03f) {
|
Bat(IGame *game, int entity) : Enemy(game, entity, 1, 102, 0.0f, 0.03f) {
|
||||||
stand = STAND_AIR;
|
stand = STAND_AIR;
|
||||||
stepHeight = 20 * 1024;
|
stepHeight = 20 * 1024;
|
||||||
dropHeight = -20 * 1024;
|
dropHeight = -20 * 1024;
|
||||||
@@ -837,4 +869,236 @@ struct Bat : Enemy {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#define REX_DIST_BITE 1500
|
||||||
|
#define REX_DIST_BITE_MAX 4096
|
||||||
|
#define REX_DIST_WALK 5120
|
||||||
|
#define REX_TURN_FAST (DEG2RAD * 120)
|
||||||
|
#define REX_TURN_SLOW (DEG2RAD * 60)
|
||||||
|
|
||||||
|
struct Rex : Enemy {
|
||||||
|
|
||||||
|
enum {
|
||||||
|
HIT_MASK = (1 << 12) | (1 << 13), // head
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
STATE_NONE,
|
||||||
|
STATE_STOP,
|
||||||
|
STATE_WALK,
|
||||||
|
STATE_RUN,
|
||||||
|
STATE_UNUSED,
|
||||||
|
STATE_DEATH,
|
||||||
|
STATE_BAWL,
|
||||||
|
STATE_BITE,
|
||||||
|
STATE_FATAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
Rex(IGame *game, int entity) : Enemy(game, entity, 100, 341, 2000.0f, 1.0f) {
|
||||||
|
jointChest = 10;
|
||||||
|
jointHead = 12;
|
||||||
|
nextState = STATE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int getStateGround() {
|
||||||
|
if (!getEntity().flags.active)
|
||||||
|
return state;
|
||||||
|
|
||||||
|
if (!think(true))
|
||||||
|
return state;
|
||||||
|
|
||||||
|
if (nextState == state)
|
||||||
|
nextState = STATE_NONE;
|
||||||
|
|
||||||
|
if (targetDead) {
|
||||||
|
return (state == STATE_STOP || state == STATE_WALK) ? STATE_WALK : STATE_STOP;
|
||||||
|
if (state != STATE_STOP) return STATE_STOP;
|
||||||
|
return STATE_WALK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mask = collide(target);
|
||||||
|
|
||||||
|
// if Lara is behind and watching Rex we need to rotate
|
||||||
|
bool walk = targetFromView && !targetInView && mood != MOOD_ESCAPE;
|
||||||
|
if (!walk && targetCanAttack && targetDist > REX_DIST_BITE && targetDist < REX_DIST_BITE_MAX)
|
||||||
|
walk = true;
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case STATE_STOP :
|
||||||
|
if (nextState != STATE_NONE) return nextState;
|
||||||
|
if (targetCanAttack && targetDist < REX_DIST_BITE) return STATE_BITE;
|
||||||
|
if (mood == MOOD_SLEEP || walk) return STATE_WALK;
|
||||||
|
return STATE_RUN;
|
||||||
|
case STATE_WALK :
|
||||||
|
if (mask) target->hit(1, this);
|
||||||
|
if (mood != MOOD_SLEEP && !walk) return STATE_STOP;
|
||||||
|
if (targetInView && randf() < 0.015f) {
|
||||||
|
nextState = STATE_BAWL;
|
||||||
|
return STATE_STOP;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case STATE_RUN :
|
||||||
|
if (mask) target->hit(10, this);
|
||||||
|
if ((targetCanAttack && targetDist < REX_DIST_WALK) || walk)
|
||||||
|
return STATE_STOP;
|
||||||
|
if (targetInView && mood != MOOD_ESCAPE && randf() < 0.015f) {
|
||||||
|
nextState = STATE_BAWL;
|
||||||
|
return STATE_STOP;
|
||||||
|
}
|
||||||
|
if (mood == MOOD_SLEEP)
|
||||||
|
return STATE_STOP;
|
||||||
|
break;
|
||||||
|
case STATE_BITE :
|
||||||
|
if (mask & HIT_MASK) {
|
||||||
|
target->hit(10000, this);
|
||||||
|
return STATE_FATAL;
|
||||||
|
}
|
||||||
|
nextState = STATE_WALK;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int getStateDeath() {
|
||||||
|
return state == STATE_STOP ? STATE_DEATH : STATE_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void updatePosition() {
|
||||||
|
if (state == STATE_DEATH) {
|
||||||
|
animation.overrideMask = 0;
|
||||||
|
angle.z = 0.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float angleY = 0.0f;
|
||||||
|
getTargetInfo(0, NULL, NULL, &angleY, NULL);
|
||||||
|
|
||||||
|
if (state == STATE_RUN || state == STATE_WALK)
|
||||||
|
turn(angleY, state == STATE_RUN ? REX_TURN_FAST : REX_TURN_SLOW);
|
||||||
|
|
||||||
|
Enemy::updatePosition();
|
||||||
|
setOverrides(true, jointChest, jointHead);
|
||||||
|
lookAt(target ? target->entity : -1, jointChest, jointHead, targetInView && state != STATE_DEATH && state != STATE_FATAL);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RAPTOR_DIST_BITE 680
|
||||||
|
#define RAPTOR_DIST_ATTACK (1024 + 512)
|
||||||
|
|
||||||
|
#define RAPTOR_TURN_FAST (DEG2RAD * 120)
|
||||||
|
#define RAPTOR_TURN_SLOW (DEG2RAD * 30)
|
||||||
|
|
||||||
|
struct Raptor : Enemy {
|
||||||
|
|
||||||
|
enum {
|
||||||
|
HIT_MASK = 0xFF7C00, // hands and head
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ANIM_DEATH_1 = 9,
|
||||||
|
ANIM_DEATH_2 = 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
STATE_NONE = -1,
|
||||||
|
STATE_DEATH,
|
||||||
|
STATE_STOP,
|
||||||
|
STATE_WALK,
|
||||||
|
STATE_RUN,
|
||||||
|
STATE_ATTACK_1,
|
||||||
|
STATE_UNUSED,
|
||||||
|
STATE_BAWL,
|
||||||
|
STATE_ATTACK_2,
|
||||||
|
STATE_BITE,
|
||||||
|
};
|
||||||
|
|
||||||
|
Raptor(IGame *game, int entity) : Enemy(game, entity, 20, 341, 400.0f, 0.5f) {
|
||||||
|
jointChest = -1;
|
||||||
|
jointHead = 21;
|
||||||
|
nextState = STATE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int getStateGround() {
|
||||||
|
if (!getEntity().flags.active)
|
||||||
|
return state;
|
||||||
|
|
||||||
|
if (!think(true))
|
||||||
|
return state;
|
||||||
|
|
||||||
|
if (nextState == state)
|
||||||
|
nextState = STATE_NONE;
|
||||||
|
|
||||||
|
if (targetDead) {
|
||||||
|
return (state == STATE_STOP || state == STATE_WALK) ? STATE_WALK : STATE_STOP;
|
||||||
|
if (state != STATE_STOP) return STATE_STOP;
|
||||||
|
return STATE_WALK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mask = collide(target);
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case STATE_STOP :
|
||||||
|
if (nextState != STATE_NONE) return nextState;
|
||||||
|
if ((mask & HIT_MASK) || (targetCanAttack && targetDist < RAPTOR_DIST_BITE)) return STATE_BITE;
|
||||||
|
if (targetCanAttack && targetDist < RAPTOR_DIST_ATTACK) return STATE_ATTACK_1;
|
||||||
|
if (mood == MOOD_SLEEP) return STATE_WALK;
|
||||||
|
return STATE_RUN;
|
||||||
|
case STATE_WALK :
|
||||||
|
if (nextState != STATE_NONE) return STATE_STOP;
|
||||||
|
if (mood != MOOD_SLEEP) return STATE_STOP;
|
||||||
|
if (targetInView && randf() < 0.01f) {
|
||||||
|
nextState = STATE_BAWL;
|
||||||
|
return STATE_STOP;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case STATE_RUN :
|
||||||
|
if (nextState != STATE_NONE) return STATE_STOP;
|
||||||
|
if (mask & HIT_MASK) return STATE_STOP;
|
||||||
|
if (targetCanAttack && targetDist < RAPTOR_DIST_ATTACK)
|
||||||
|
return (randf() < 0.25) ? STATE_STOP : STATE_ATTACK_2;
|
||||||
|
if (mood == MOOD_ESCAPE && targetInView) {
|
||||||
|
nextState = STATE_BAWL;
|
||||||
|
return STATE_STOP;
|
||||||
|
}
|
||||||
|
if (mood == MOOD_SLEEP)
|
||||||
|
return STATE_STOP;
|
||||||
|
break;
|
||||||
|
case STATE_ATTACK_1 :
|
||||||
|
case STATE_ATTACK_2 :
|
||||||
|
case STATE_BITE :
|
||||||
|
if (nextState == STATE_NONE && targetInView && (mask & HIT_MASK)) {
|
||||||
|
bite(animation.getJoints(getMatrix(), jointHead, true).pos, 100);
|
||||||
|
nextState = state == STATE_ATTACK_2 ? STATE_RUN : STATE_STOP;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int getStateDeath() {
|
||||||
|
if (state == STATE_DEATH) return state;
|
||||||
|
return animation.setAnim((rand() % 2) ? ANIM_DEATH_1 : ANIM_DEATH_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void updatePosition() {
|
||||||
|
if (state == STATE_DEATH) {
|
||||||
|
animation.overrideMask = 0;
|
||||||
|
angle.z = 0.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float angleY = 0.0f;
|
||||||
|
getTargetInfo(0, NULL, NULL, &angleY, NULL);
|
||||||
|
|
||||||
|
if (state == STATE_RUN || state == STATE_WALK)
|
||||||
|
turn(angleY, state == STATE_RUN ? RAPTOR_TURN_FAST : RAPTOR_TURN_SLOW);
|
||||||
|
|
||||||
|
Enemy::updatePosition();
|
||||||
|
setOverrides(true, jointChest, jointHead);
|
||||||
|
lookAt(target ? target->entity : -1, jointChest, jointHead, targetInView && state != STATE_DEATH);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
13
src/format.h
13
src/format.h
@@ -602,11 +602,8 @@ namespace TR {
|
|||||||
|
|
||||||
struct Entity {
|
struct Entity {
|
||||||
|
|
||||||
typedef int16 Type;
|
enum Type : int16 { NONE = -1, TR1_TYPES(DECL_ENUM) } type;
|
||||||
|
|
||||||
enum { NONE = -1, TR1_TYPES(DECL_ENUM) };
|
|
||||||
|
|
||||||
int16 type;
|
|
||||||
int16 room;
|
int16 room;
|
||||||
int32 x, y, z;
|
int32 x, y, z;
|
||||||
angle rotation;
|
angle rotation;
|
||||||
@@ -624,6 +621,14 @@ namespace TR {
|
|||||||
return type >= ENEMY_TWIN && type <= ENEMY_LARSON;
|
return type >= ENEMY_TWIN && type <= ENEMY_LARSON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isBigEnemy() {
|
||||||
|
return type == ENEMY_REX || type == ENEMY_MUTANT_1 || type == ENEMY_CENTAUR;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDoor() {
|
||||||
|
return (type >= DOOR_1 && type <= DOOR_6) || type == DOOR_LIFT;
|
||||||
|
}
|
||||||
|
|
||||||
int isItem() {
|
int isItem() {
|
||||||
return (type >= WEAPON_PISTOLS && type <= AMMO_UZIS) ||
|
return (type >= WEAPON_PISTOLS && type <= AMMO_UZIS) ||
|
||||||
(type >= PUZZLE_1 && type <= PUZZLE_4) ||
|
(type >= PUZZLE_1 && type <= PUZZLE_4) ||
|
||||||
|
96
src/lara.h
96
src/lara.h
@@ -1135,6 +1135,16 @@ struct Lara : Character {
|
|||||||
|
|
||||||
virtual void hit(int damage, Controller *enemy = NULL) {
|
virtual void hit(int damage, Controller *enemy = NULL) {
|
||||||
health -= damage;
|
health -= damage;
|
||||||
|
if (damage == 10000) { // T-Rex attack (fatal)
|
||||||
|
pos = enemy->pos;
|
||||||
|
angle = enemy->angle;
|
||||||
|
|
||||||
|
meshSwap(1, TR::MODEL_LARA_SPEC, BODY_UPPER | BODY_LOWER);
|
||||||
|
meshSwap(2, level->extra.weapons[Weapon::SHOTGUN], 0);
|
||||||
|
meshSwap(3, level->extra.weapons[Weapon::UZIS], 0);
|
||||||
|
|
||||||
|
animation.setAnim(level->models[TR::MODEL_LARA_SPEC].animation + 1);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool waterOut() {
|
bool waterOut() {
|
||||||
@@ -1864,8 +1874,6 @@ struct Lara : Character {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void update() {
|
virtual void update() {
|
||||||
collisionOffset = vec3(0.0f);
|
|
||||||
checkCollisions();
|
|
||||||
Character::update();
|
Character::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1998,7 +2006,9 @@ struct Lara : Character {
|
|||||||
vTilt *= rotFactor.y;
|
vTilt *= rotFactor.y;
|
||||||
updateTilt(state == STATE_RUN || stand == STAND_UNDERWATER, vTilt.x, vTilt.y);
|
updateTilt(state == STATE_RUN || stand == STAND_UNDERWATER, vTilt.x, vTilt.y);
|
||||||
|
|
||||||
if ((velocity + collisionOffset).length2() >= 1.0f)
|
collisionOffset = vec3(0.0f);
|
||||||
|
|
||||||
|
if (checkCollisions() || (velocity + collisionOffset).length2() >= 1.0f)
|
||||||
move();
|
move();
|
||||||
|
|
||||||
if (getEntity().type != TR::Entity::LARA) {
|
if (getEntity().type != TR::Entity::LARA) {
|
||||||
@@ -2019,7 +2029,7 @@ struct Lara : Character {
|
|||||||
return getEntity().type == TR::Entity::LARA ? pos : chestOffset;
|
return getEntity().type == TR::Entity::LARA ? pos : chestOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkCollisions() {
|
bool checkCollisions() {
|
||||||
// check static objects (TODO: check linked rooms?)
|
// check static objects (TODO: check linked rooms?)
|
||||||
TR::Room &room = getRoom();
|
TR::Room &room = getRoom();
|
||||||
Box box(pos - vec3(LARA_RADIUS, LARA_HEIGHT, LARA_RADIUS), pos + vec3(LARA_RADIUS, 0.0f, LARA_RADIUS));
|
Box box(pos - vec3(LARA_RADIUS, LARA_HEIGHT, LARA_RADIUS), pos + vec3(LARA_RADIUS, 0.0f, LARA_RADIUS));
|
||||||
@@ -2038,27 +2048,40 @@ struct Lara : Character {
|
|||||||
|
|
||||||
if (!canHitAnim()) {
|
if (!canHitAnim()) {
|
||||||
hitDir = -1;
|
hitDir = -1;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check enemies
|
// check enemies & doors
|
||||||
for (int i = 0; i < level->entitiesCount; i++) {
|
for (int i = 0; i < level->entitiesCount; i++) {
|
||||||
TR::Entity &e = level->entities[i];
|
TR::Entity &e = level->entities[i];
|
||||||
if (!e.flags.active || !e.isEnemy()) continue;
|
Controller *controller = (Controller*)e.controller;
|
||||||
Character *enemy = (Character*)e.controller;
|
|
||||||
if (enemy->health <= 0) continue;
|
|
||||||
|
|
||||||
vec3 dir = pos - vec3(0.0f, 128.0f, 0.0f) - enemy->pos;
|
if (e.isEnemy()) {
|
||||||
vec3 p = dir.rotateY(-enemy->angle.y);
|
if (e.type != TR::Entity::ENEMY_REX && (!e.flags.active || ((Character*)e.controller)->health <= 0)) continue;
|
||||||
|
} else
|
||||||
|
if (!e.isDoor()) continue;
|
||||||
|
|
||||||
Box enemyBox = enemy->getBoundingBoxLocal();
|
vec3 dir = pos - vec3(0.0f, 128.0f, 0.0f) - controller->pos;
|
||||||
if (!enemyBox.contains(p))
|
vec3 p = dir.rotateY(controller->angle.y);
|
||||||
|
|
||||||
|
Box box = controller->getBoundingBoxLocal();
|
||||||
|
if (!box.contains(p))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// get shift
|
if (e.isEnemy()) { // enemy collision
|
||||||
p += enemyBox.pushOut2D(p);
|
if (!collide(controller, false))
|
||||||
p = (p.rotateY(enemy->angle.y) + enemy->pos) - pos;
|
continue;
|
||||||
collisionOffset += vec3(p.x, 0.0f, p.z);
|
velocity.x = velocity.y = 0.0f;
|
||||||
|
} else { // door collision
|
||||||
|
p += box.pushOut2D(p);
|
||||||
|
p = (p.rotateY(-controller->angle.y) + controller->pos) - pos;
|
||||||
|
collisionOffset += vec3(p.x, 0.0f, p.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.type == TR::Entity::ENEMY_REX && ((Character*)e.controller)->health <= 0)
|
||||||
|
return true;
|
||||||
|
if (e.isDoor())
|
||||||
|
return true;
|
||||||
|
|
||||||
// get hit dir
|
// get hit dir
|
||||||
if (hitDir == -1) {
|
if (hitDir == -1) {
|
||||||
@@ -2068,10 +2091,11 @@ struct Lara : Character {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hitDir = angleQuadrant(dir.rotateY(angle.y + PI * 0.5f).angleY());
|
hitDir = angleQuadrant(dir.rotateY(angle.y + PI * 0.5f).angleY());
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
hitDir = -1;
|
hitDir = -1;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void move() {
|
void move() {
|
||||||
@@ -2126,42 +2150,6 @@ struct Lara : Character {
|
|||||||
pos = opos;
|
pos = opos;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
TR::Animation *anim = animation;
|
|
||||||
Box eBox = Box(pos - vec3(128.0f, 0.0f, 128.0f), pos + vec3(128.0, getHeight(), 128.0f)); // getBoundingBox();
|
|
||||||
// check static meshes in the room
|
|
||||||
if (canPassGap) {
|
|
||||||
TR::Room &r = level->rooms[e.room];
|
|
||||||
for (int i = 0; i < r.meshesCount; i++) {
|
|
||||||
TR::Room::Mesh &m = r.meshes[i];
|
|
||||||
TR::StaticMesh *sm = level->getMeshByID(m.meshID);
|
|
||||||
if (sm->flags != 2) continue; // no have collision box
|
|
||||||
|
|
||||||
Box mBox;
|
|
||||||
vec3 offset(m.x, m.y, m.z);
|
|
||||||
sm->getBox(true, m.rotation, mBox);
|
|
||||||
mBox.min += offset;
|
|
||||||
mBox.max += offset;
|
|
||||||
|
|
||||||
if (eBox.intersect(mBox)) {
|
|
||||||
canPassGap = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check entities in the room
|
|
||||||
if (canPassGap)
|
|
||||||
for (int i = 0; i < level->entitiesCount; i++)
|
|
||||||
if (i != entity && level->entities[i].room == e.room && level->entities[i].controller) {
|
|
||||||
Box mBox = ((Controller*)level->entities[i].controller)->getBoundingBox();
|
|
||||||
if (eBox.intersect(mBox)) {
|
|
||||||
canPassGap = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// get current leading foot in animation
|
// get current leading foot in animation
|
||||||
int rightStart = 0;
|
int rightStart = 0;
|
||||||
if (state == STATE_RUN) rightStart = 6;
|
if (state == STATE_RUN) rightStart = 6;
|
||||||
|
15
src/level.h
15
src/level.h
@@ -57,8 +57,8 @@ struct Level : IGame {
|
|||||||
return item->boxes[int(randf() * item->count)];
|
return item->boxes[int(randf() * item->count)];
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual uint16 findPath(int ascend, int descend, int boxStart, int boxEnd, uint16 *zones, uint16 **boxes) {
|
virtual uint16 findPath(int ascend, int descend, bool big, int boxStart, int boxEnd, uint16 *zones, uint16 **boxes) {
|
||||||
return zoneCache->findPath(ascend, descend, boxStart, boxEnd, zones, boxes);
|
return zoneCache->findPath(ascend, descend, big, boxStart, boxEnd, zones, boxes);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void setClipParams(float clipSign, float clipHeight) {
|
virtual void setClipParams(float clipSign, float clipHeight) {
|
||||||
@@ -107,6 +107,10 @@ struct Level : IGame {
|
|||||||
Core::pass = Core::passCompose;
|
Core::pass = Core::passCompose;
|
||||||
renderScene(roomIndex);
|
renderScene(roomIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void fxQuake(float time) {
|
||||||
|
camera->shake = time;
|
||||||
|
}
|
||||||
//==============================
|
//==============================
|
||||||
|
|
||||||
Level(Stream &stream, Stream *snd, bool demo, bool home) : level(stream, demo), lara(NULL) {
|
Level(Stream &stream, Stream *snd, bool demo, bool home) : level(stream, demo), lara(NULL) {
|
||||||
@@ -147,14 +151,18 @@ struct Level : IGame {
|
|||||||
case TR::Entity::ENEMY_RAT_LAND :
|
case TR::Entity::ENEMY_RAT_LAND :
|
||||||
case TR::Entity::ENEMY_RAT_WATER :
|
case TR::Entity::ENEMY_RAT_WATER :
|
||||||
case TR::Entity::ENEMY_REX :
|
case TR::Entity::ENEMY_REX :
|
||||||
|
entity.controller = new Rex(this, i);
|
||||||
|
break;
|
||||||
case TR::Entity::ENEMY_RAPTOR :
|
case TR::Entity::ENEMY_RAPTOR :
|
||||||
|
entity.controller = new Raptor(this, i);
|
||||||
|
break;
|
||||||
case TR::Entity::ENEMY_MUTANT_1 :
|
case TR::Entity::ENEMY_MUTANT_1 :
|
||||||
case TR::Entity::ENEMY_MUTANT_2 :
|
case TR::Entity::ENEMY_MUTANT_2 :
|
||||||
case TR::Entity::ENEMY_MUTANT_3 :
|
case TR::Entity::ENEMY_MUTANT_3 :
|
||||||
case TR::Entity::ENEMY_CENTAUR :
|
case TR::Entity::ENEMY_CENTAUR :
|
||||||
case TR::Entity::ENEMY_MUMMY :
|
case TR::Entity::ENEMY_MUMMY :
|
||||||
case TR::Entity::ENEMY_LARSON :
|
case TR::Entity::ENEMY_LARSON :
|
||||||
entity.controller = new Enemy(this, i, 100, 10, 0.0f);
|
entity.controller = new Enemy(this, i, 100, 10, 0.0f, 0.0f);
|
||||||
break;
|
break;
|
||||||
case TR::Entity::DOOR_1 :
|
case TR::Entity::DOOR_1 :
|
||||||
case TR::Entity::DOOR_2 :
|
case TR::Entity::DOOR_2 :
|
||||||
@@ -205,7 +213,6 @@ struct Level : IGame {
|
|||||||
case TR::Entity::MOVING_BLOCK :
|
case TR::Entity::MOVING_BLOCK :
|
||||||
entity.controller = new MovingBlock(this, i);
|
entity.controller = new MovingBlock(this, i);
|
||||||
break;
|
break;
|
||||||
case 1592 :
|
|
||||||
case TR::Entity::FALLING_CEILING_1 :
|
case TR::Entity::FALLING_CEILING_1 :
|
||||||
case TR::Entity::FALLING_CEILING_2 :
|
case TR::Entity::FALLING_CEILING_2 :
|
||||||
case TR::Entity::FALLING_SWORD :
|
case TR::Entity::FALLING_SWORD :
|
||||||
|
Reference in New Issue
Block a user