1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-13 16:44:50 +02:00

#22 mutant eggs; #14 explode mutants, add hit sounds, fix blob shadows overlay

This commit is contained in:
XProger
2017-09-28 04:15:17 +03:00
parent 3acc52134e
commit 95fb64b2cf
8 changed files with 381 additions and 33 deletions

View File

@@ -31,7 +31,6 @@ struct Camera : ICamera {
int room; int room;
float timer; float timer;
float shake;
Basis prevBasis; Basis prevBasis;
@@ -43,7 +42,7 @@ struct Camera : ICamera {
bool firstPerson; bool firstPerson;
bool isVR; bool isVR;
Camera(IGame *game, Character *owner) : ICamera(), game(game), level(game->getLevel()), owner(owner), frustum(new Frustum()), timer(-1.0f), shake(0.0f), viewIndex(-1), viewIndexLast(-1), viewTarget(NULL), isVR(false) { Camera(IGame *game, Character *owner) : ICamera(), game(game), level(game->getLevel()), owner(owner), frustum(new Frustum()), timer(-1.0f), viewIndex(-1), viewIndexLast(-1), viewTarget(NULL), isVR(false) {
changeView(false); changeView(false);
if (owner->getEntity().type != TR::Entity::LARA && level->cameraFrames) { if (owner->getEntity().type != TR::Entity::LARA && level->cameraFrames) {
state = STATE_CUTSCENE; state = STATE_CUTSCENE;

View File

@@ -17,10 +17,11 @@
struct Controller; struct Controller;
struct ICamera { struct ICamera {
vec4 *reflectPlane; vec4 *reflectPlane;
vec3 pos; vec3 pos;
float shake;
ICamera() : reflectPlane(NULL) {} ICamera() : reflectPlane(NULL), pos(0.0f), shake(0.0f) {}
virtual void setup(bool calcMatrices) {} virtual void setup(bool calcMatrices) {}
virtual int getRoomIndex() const { return TR::NO_ROOM; } virtual int getRoomIndex() const { return TR::NO_ROOM; }
@@ -52,6 +53,9 @@ struct IGame {
virtual void checkTrigger(Controller *controller, bool heavy) {} virtual void checkTrigger(Controller *controller, bool heavy) {}
virtual int addSprite(TR::Entity::Type type, int room, int x, int y, int z, int frame = -1, bool empty = false) { return -1; }
virtual int addEnemy(TR::Entity::Type type, int room, const vec3 &pos, float angle) { return -1; }
virtual bool invUse(TR::Entity::Type type) { return false; } virtual bool invUse(TR::Entity::Type type) { return false; }
virtual void invAdd(TR::Entity::Type type, int count = 1) {} virtual void invAdd(TR::Entity::Type type, int count = 1) {}
virtual int* invCount(TR::Entity::Type type) { return NULL; } virtual int* invCount(TR::Entity::Type type) { return NULL; }
@@ -65,7 +69,7 @@ struct IGame {
struct Controller { struct Controller {
static Controller *first; static Controller *first;
Controller *next; Controller *next;
enum ActiveState { asNone, asActive, asInactive } activeState; enum ActiveState { asNone, asActive, asInactive };
IGame *game; IGame *game;
TR::Level *level; TR::Level *level;
@@ -94,7 +98,17 @@ struct Controller {
uint32 mask; uint32 mask;
} *layers; } *layers;
Controller(IGame *game, int entity) : next(NULL), activeState(asNone), game(game), level(game->getLevel()), entity(entity), animation(level, getModel()), state(animation.state), layers(NULL) { uint32 explodeMask;
struct ExplodePart {
Basis basis;
vec3 velocity;
int roomIndex;
} *explodeParts;
ActiveState activeState;
bool invertAim;
Controller(IGame *game, int entity) : next(NULL), activeState(asNone), game(game), level(game->getLevel()), entity(entity), animation(level, getModel()), state(animation.state), layers(0), explodeMask(0), explodeParts(0), invertAim(false) {
TR::Entity &e = getEntity(); TR::Entity &e = getEntity();
pos = vec3(float(e.x), float(e.y), float(e.z)); pos = vec3(float(e.x), float(e.y), float(e.z));
angle = vec3(0.0f, e.rotation, 0.0f); angle = vec3(0.0f, e.rotation, 0.0f);
@@ -125,6 +139,7 @@ struct Controller {
virtual ~Controller() { virtual ~Controller() {
delete[] joints; delete[] joints;
delete[] layers; delete[] layers;
delete[] explodeParts;
deactivate(true); deactivate(true);
} }
@@ -195,6 +210,7 @@ struct Controller {
} }
void initMeshOverrides() { void initMeshOverrides() {
if (layers) return;
layers = new MeshLayer[MAX_LAYERS]; layers = new MeshLayer[MAX_LAYERS];
memset(layers, 0, sizeof(MeshLayer) * MAX_LAYERS); memset(layers, 0, sizeof(MeshLayer) * MAX_LAYERS);
layers[0].model = getEntity().modelIndex - 1; layers[0].model = getEntity().modelIndex - 1;
@@ -223,6 +239,8 @@ struct Controller {
Basis b = animation.getJoints(Basis(getMatrix()), joint); Basis b = animation.getJoints(Basis(getMatrix()), joint);
vec3 delta = (b.inverse() * t).normal(); vec3 delta = (b.inverse() * t).normal();
if (invertAim)
delta = -delta;
float angleY = clampAngle(atan2f(delta.x, delta.z)); float angleY = clampAngle(atan2f(delta.x, delta.z));
float angleX = clampAngle(asinf(delta.y)); float angleX = clampAngle(asinf(delta.y));
@@ -526,8 +544,37 @@ struct Controller {
animation.framePrev = animation.frameIndex; animation.framePrev = animation.frameIndex;
} }
void updateExplosion() {
if (!explodeMask) return;
TR::Model *model = getModel();
for (int i = 0; i < model->mCount; i++)
if (explodeMask & (1 << i)) {
ExplodePart &part = explodeParts[i];
part.velocity.y += GRAVITY * Core::deltaTime;
quat q = quat(vec3(1, 0, 0), PI * Core::deltaTime) * quat(vec3(0, 0, 1), PI * 2.0f * Core::deltaTime);
part.basis = Basis(part.basis.rot * q, part.basis.pos + explodeParts[i].velocity * (Core::deltaTime * 30.0f));
vec3 p = part.basis.pos;
TR::Room::Sector *s = level->getSector(part.roomIndex, int(p.x), int(p.y), int(p.z));
if (s && s->floor * 256.0f < p.y) {
explodeMask &= ~(1 << i);
game->addSprite(TR::Entity::EXPLOSION, part.roomIndex, int(p.x), int(p.y), int(p.z));
game->playSound(TR::SND_EXPLOSION, pos, 0); // Sound::Flags::PAN ?
}
}
if (!explodeMask) {
delete[] explodeParts;
explodeParts = NULL;
}
}
virtual void update() { virtual void update() {
updateAnimation(true); updateAnimation(true);
updateExplosion();
} }
void updateLights(bool lerp = true) { void updateLights(bool lerp = true) {
@@ -587,6 +634,34 @@ struct Controller {
return matrix; return matrix;
} }
void explode(int32 mask) {
TR::Model *model = getModel();
if (!layers) initMeshOverrides();
mask &= layers[0].mask;
layers[0].mask &= ~mask;
explodeParts = new ExplodePart[model->mCount];
explodeMask = 0;
animation.getJoints(getMatrix(), -1, true, joints);
int roomIndex = getRoomIndex();
for (int i = 0; i < model->mCount; i++) {
if (!(mask & (1 << i)))
continue;
explodeMask |= (1 << i);
float angle = randf() * PI * 2.0f;
float speed = randf() * 256.0f;
ExplodePart &part = explodeParts[i];
part.basis = joints[i];
part.basis.w = 1.0f;
part.velocity = vec3(cosf(angle), (randf() - 0.5f) * 0.25f, sinf(angle)) * speed;
part.roomIndex = roomIndex;
}
}
void renderShadow(MeshBuilder *mesh) { void renderShadow(MeshBuilder *mesh) {
TR::Entity &entity = getEntity(); TR::Entity &entity = getEntity();
@@ -621,16 +696,18 @@ struct Controller {
Core::active.shader->setParam(uMaterial, vec4(vec3(0.5f * (1.0f - alpha)), alpha)); Core::active.shader->setParam(uMaterial, vec4(vec3(0.5f * (1.0f - alpha)), alpha));
Core::active.shader->setParam(uAmbient, vec3(0.0f)); Core::active.shader->setParam(uAmbient, vec3(0.0f));
Core::setDepthWrite(false);
Core::setBlending(bmMultiply); Core::setBlending(bmMultiply);
mesh->renderShadowBlob(); mesh->renderShadowBlob();
Core::setBlending(bmNone); Core::setBlending(bmNone);
Core::setDepthWrite(true);
} }
virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { // TODO: animation.calcJoints virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { // TODO: animation.calcJoints
mat4 matrix = getMatrix(); mat4 matrix = getMatrix();
Box box = animation.getBoundingBox(vec3(0, 0, 0), 0); Box box = animation.getBoundingBox(vec3(0, 0, 0), 0);
if (frustum && !frustum->isVisible(matrix, box.min, box.max)) if (!explodeMask && frustum && !frustum->isVisible(matrix, box.min, box.max))
return; return;
TR::Entity &entity = getEntity(); TR::Entity &entity = getEntity();
@@ -643,16 +720,23 @@ struct Controller {
animation.getJoints(matrix, -1, true, joints); animation.getJoints(matrix, -1, true, joints);
if (layers) { if (layers) {
int mask = 0; uint32 mask = 0;
for (int i = MAX_LAYERS - 1; i >= 0; i--) { for (int i = MAX_LAYERS - 1; i >= 0; i--) {
int vmask = layers[i].mask & ~mask; uint32 vmask = (layers[i].mask & ~mask) | (!i ? explodeMask : 0);
if (!vmask) continue; if (!vmask) continue;
mask |= layers[i].mask; mask |= layers[i].mask;
// set meshes visibility // set meshes visibility
for (int j = 0; j < model->mCount; j++) for (int j = 0; j < model->mCount; j++)
joints[j].w = (vmask & (1 << j)) ? 1.0f : -1.0f; // AHAHA joints[j].w = (vmask & (1 << j)) ? 1.0f : -1.0f; // AHAHA
if (explodeMask) {
TR::Model *model = getModel();
for (int i = 0; i < model->mCount; i++)
if (explodeMask & (1 << i))
joints[i] = explodeParts[i].basis;
}
// if (entity.type == TR::Entity::LARA && Core::eye != 0) // if (entity.type == TR::Entity::LARA && Core::eye != 0)
// joints[14].w = -1.0f; // joints[14].w = -1.0f;
// render // render

View File

@@ -57,6 +57,7 @@ struct Enemy : Character {
float length; // dist from center to head (jaws) float length; // dist from center to head (jaws)
float aggression; float aggression;
int radius; int radius;
int hitSound;
Character *target; Character *target;
Path *path; Path *path;
@@ -85,7 +86,13 @@ struct Enemy : Character {
} }
virtual void updateVelocity() { virtual void updateVelocity() {
velocity = getDir() * animation.getSpeed(); if (stand == STAND_AIR && (!flying || health <= 0.0f))
velocity.y += GRAVITY * Core::deltaTime;
else
velocity = getDir() * animation.getSpeed();
if (health <= 0.0f)
velocity.x = velocity.y = 0.0f;
} }
bool checkPoint(int x, int z) { bool checkPoint(int x, int z) {
@@ -126,7 +133,7 @@ struct Enemy : Character {
if (px != nx) pos.x = float(nx); if (px != nx) pos.x = float(nx);
if (pz != nz) pos.z = float(nz); if (pz != nz) pos.z = float(nz);
} }
virtual void updatePosition() { virtual void updatePosition() {
if (!getEntity().flags.active) return; if (!getEntity().flags.active) return;
@@ -137,7 +144,11 @@ struct Enemy : Character {
clipByBox(pos); clipByBox(pos);
TR::Level::FloorInfo info; TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z, info); level->getFloorInfo(getRoomIndex(), int(pos.x), int(pos.y), int(pos.z), info);
if (stand == STAND_AIR && !flying && info.floor < pos.y) {
stand = STAND_GROUND;
pos.y = info.floor;
}
if (info.boxIndex != 0xFFFF && zone == getZones()[info.boxIndex] && !level->boxes[info.boxIndex].overlap.block) { if (info.boxIndex != 0xFFFF && zone == getZones()[info.boxIndex] && !level->boxes[info.boxIndex].overlap.block) {
switch (stand) { switch (stand) {
@@ -221,6 +232,8 @@ struct Enemy : Character {
virtual void hit(float damage, Controller *enemy = NULL, TR::HitType hitType = TR::HIT_DEFAULT) { virtual void hit(float damage, Controller *enemy = NULL, TR::HitType hitType = TR::HIT_DEFAULT) {
Character::hit(damage, enemy, hitType); Character::hit(damage, enemy, hitType);
wound = true; wound = true;
if (hitSound > -1)
game->playSound(hitSound, pos, Sound::PAN);
}; };
void bite(const vec3 &pos, float damage) { void bite(const vec3 &pos, float damage) {
@@ -486,6 +499,7 @@ struct Wolf : Enemy {
dropHeight = -1024; dropHeight = -1024;
jointChest = 2; jointChest = 2;
jointHead = 3; jointHead = 3;
hitSound = TR::SND_HIT_WOLF;
nextState = STATE_NONE; nextState = STATE_NONE;
animation.time = animation.timeMax; animation.time = animation.timeMax;
updateAnimation(false); updateAnimation(false);
@@ -605,6 +619,18 @@ struct Wolf : Enemy {
} }
}; };
struct Lion : Enemy {
Lion(IGame *game, int entity) : Enemy(game, entity, 6, 341, 375.0f, 0.25f) {
hitSound = TR::SND_HIT_LION;
}
};
struct Rat : Enemy {
Rat(IGame *game, int entity) : Enemy(game, entity, 6, 341, 375.0f, 0.25f) {
hitSound = TR::SND_HIT_RAT;
}
};
#define BEAR_DIST_EAT 768 #define BEAR_DIST_EAT 768
#define BEAR_DIST_HOWL 2048 #define BEAR_DIST_HOWL 2048
#define BEAR_DIST_BITE 1024 #define BEAR_DIST_BITE 1024
@@ -640,7 +666,8 @@ struct Bear : Enemy {
Bear(IGame *game, int entity) : Enemy(game, entity, 20, 341, 500.0f, 0.5f) { Bear(IGame *game, int entity) : Enemy(game, entity, 20, 341, 500.0f, 0.5f) {
jointChest = 13; jointChest = 13;
jointHead = 14; jointHead = 14;
hitSound = TR::SND_HIT_BEAR;
nextState = STATE_NONE; nextState = STATE_NONE;
} }
@@ -814,13 +841,6 @@ struct Bat : Enemy {
return state == STATE_DEATH ? state : animation.setAnim(ANIM_DEATH); return state == STATE_DEATH ? state : animation.setAnim(ANIM_DEATH);
} }
virtual void updateVelocity() {
if (state != STATE_DEATH)
Enemy::updateVelocity();
else
velocity = vec3(0.0f, velocity.y + GRAVITY * Core::deltaTime, 0.0f);
}
virtual void updatePosition() { virtual void updatePosition() {
float angleY = 0.0f; float angleY = 0.0f;
if (state == STATE_FLY || state == STATE_ATTACK) if (state == STATE_FLY || state == STATE_ATTACK)
@@ -1065,4 +1085,121 @@ struct Raptor : Enemy {
} }
}; };
#endif struct Mutant : Enemy {
Mutant(IGame *game, int entity) : Enemy(game, entity, 50, 341, 150.0f, 1.0f) {
TR::Entity &e = getEntity();
if (e.type != TR::Entity::ENEMY_MUTANT_1) {
initMeshOverrides();
layers[0].mask = 0xffe07fff;
aggression = 0.25f;
}
jointChest = 1;
jointHead = 2;
nextState = 0;
}
virtual void update() {
bool exploded = explodeMask != 0;
if (health <= 0.0f && !exploded) {
game->playSound(TR::SND_MUTANT_DEATH, pos, 0);
explode(0xffffffff);
}
Enemy::update();
if (exploded && !explodeMask) {
deactivate(true);
getEntity().flags.invisible = true;
}
}
virtual int getStateGround() {
if (!think(true))
return state;
return state;
}
virtual void updatePosition() {
Enemy::updatePosition();
setOverrides(true, jointChest, jointHead);
lookAt(target);
}
};
struct GiantMutant : Enemy {
enum {
STATE_STOP = 1,
STATE_BORN = 8,
STATE_FALL = 9,
};
GiantMutant(IGame *game, int entity) : Enemy(game, entity, 500, 341, 375.0f, 1.0f) {
hitSound = TR::SND_HIT_MUTANT;
stand = STAND_AIR;
jointChest = -1;
jointHead = 3;
rangeHead = vec4(-0.5f, 0.5f, -0.5f, 0.5f) * PI;
invertAim = true;
}
virtual int getStateGround() {
if (!think(true))
return state;
return state;
}
void update() {
bool exploded = explodeMask != 0;
if (health <= 0.0f && !exploded) {
game->playSound(TR::SND_MUTANT_DEATH, pos, 0);
explode(0xffffffff);
}
switch (state) {
case STATE_BORN : animation.setState(STATE_FALL); break;
case STATE_FALL :
if (stand == STAND_GROUND) {
animation.setState(STATE_STOP);
game->getCamera()->shake = 5.0f;
}
break;
}
Enemy::update();
setOverrides(true, jointChest, jointHead);
lookAt(target);
if (exploded && !explodeMask) {
game->checkTrigger(this, true);
deactivate(true);
getEntity().flags.invisible = true;
}
}
};
struct Centaur : Enemy {
Centaur(IGame *game, int entity) : Enemy(game, entity, 20, 341, 400.0f, 0.5f) {
jointChest = 10;
jointHead = 17;
}
virtual int getStateGround() {
if (!think(true))
return state;
return state;
}
virtual void updatePosition() {
Enemy::updatePosition();
setOverrides(true, jointChest, jointHead);
lookAt(target);
}
};
#endif

View File

@@ -254,6 +254,9 @@ namespace TR {
SND_SHOTGUN_RELOAD = 9, SND_SHOTGUN_RELOAD = 9,
SND_RICOCHET = 10, SND_RICOCHET = 10,
SND_HIT_BEAR = 16,
SND_HIT_WOLF = 20,
SND_SCREAM = 30, SND_SCREAM = 30,
SND_HIT = 31, SND_HIT = 31,
@@ -266,7 +269,13 @@ namespace TR {
SND_UNDERWATER = 60, SND_UNDERWATER = 60,
SND_FLOOD = 81, SND_FLOOD = 81,
SND_HIT_LION = 85,
SND_HIT_RAT = 95,
SND_EXPLOSION = 104,
SND_INV_SPIN = 108, SND_INV_SPIN = 108,
SND_INV_HOME = 109, SND_INV_HOME = 109,
SND_INV_CONTROLS = 110, SND_INV_CONTROLS = 110,
@@ -278,9 +287,15 @@ namespace TR {
SND_HEALTH = 116, SND_HEALTH = 116,
SND_STAIRS2SLOPE = 119, SND_STAIRS2SLOPE = 119,
SND_HIT_SKATEBOY = 132,
SND_HIT_MUTANT = 142,
SND_DART = 151, SND_DART = 151,
SND_EXPLOSION = 170, SND_TNT = 170,
SND_MUTANT_DEATH = 171,
SND_SECRET = 173, SND_SECRET = 173,
}; };
@@ -730,7 +745,10 @@ namespace TR {
&& type != ENEMY_REX && type != ENEMY_REX
&& type != ENEMY_RAPTOR && type != ENEMY_RAPTOR
&& type != ENEMY_MUTANT_1 && type != ENEMY_MUTANT_1
&& type != ENEMY_MUTANT_2
&& type != ENEMY_MUTANT_3
&& type != ENEMY_CENTAUR && type != ENEMY_CENTAUR
&& type != ENEMY_GIANT_MUTANT
&& type != ENEMY_MUMMY && type != ENEMY_MUMMY
&& type != ENEMY_NATLA) && type != ENEMY_NATLA)
opaque = true; opaque = true;
@@ -2016,6 +2034,9 @@ namespace TR {
} }
int16 getModelIndex(Entity::Type type) const { int16 getModelIndex(Entity::Type type) const {
if (type == TR::Entity::ENEMY_MUTANT_2 || type == TR::Entity::ENEMY_MUTANT_3)
type = TR::Entity::ENEMY_MUTANT_1; // hardcoded mutant models remapping
for (int i = 0; i < modelsCount; i++) for (int i = 0; i < modelsCount; i++)
if (type == models[i].type) if (type == models[i].type)
return i + 1; return i + 1;
@@ -2107,8 +2128,8 @@ namespace TR {
int sz = (z - room.info.z) >> 10; int sz = (z - room.info.z) >> 10;
if (sz <= 0 || sz >= room.xSectors - 1) { if (sz <= 0 || sz >= room.xSectors - 1) {
sx = clamp(sx, 1, room.xSectors - 2); sx = clamp(sx, 0, room.xSectors - 1);
sz = clamp(sz, 0, room.zSectors - 1); sz = clamp(sz, 1, room.zSectors - 2);
} else } else
sx = clamp(sx, 0, room.xSectors - 1); sx = clamp(sx, 0, room.xSectors - 1);

View File

@@ -180,8 +180,17 @@ struct Inventory {
// add(TR::Entity::INV_MEDIKIT_SMALL, 999); // add(TR::Entity::INV_MEDIKIT_SMALL, 999);
// add(TR::Entity::INV_MEDIKIT_BIG, 999); // add(TR::Entity::INV_MEDIKIT_BIG, 999);
// add(TR::Entity::INV_SCION, 1); // add(TR::Entity::INV_SCION, 1);
// add(TR::Entity::INV_KEY_1, 1); #ifdef _DEBUG
// add(TR::Entity::INV_PUZZLE_1, 1); add(TR::Entity::INV_KEY_1, 3);
add(TR::Entity::INV_KEY_2, 3);
add(TR::Entity::INV_KEY_3, 3);
add(TR::Entity::INV_KEY_4, 3);
add(TR::Entity::INV_PUZZLE_1, 3);
add(TR::Entity::INV_PUZZLE_2, 3);
add(TR::Entity::INV_PUZZLE_3, 3);
add(TR::Entity::INV_PUZZLE_4, 3);
#endif
} }
if (id == TR::TITLE) { if (id == TR::TITLE) {

View File

@@ -209,9 +209,10 @@ struct Level : IGame {
playSound(TR::SND_STAIRS2SLOPE, vec3(), 0); playSound(TR::SND_STAIRS2SLOPE, vec3(), 0);
break; break;
case TR::Effect::EXPLOSION : case TR::Effect::EXPLOSION :
playSound(TR::SND_EXPLOSION, vec3(0), 0); playSound(TR::SND_TNT, vec3(0), 0);
camera->shake = 1.0f; camera->shake = 1.0f;
break; break;
default : ;
} }
} }
@@ -219,6 +220,38 @@ struct Level : IGame {
lara->checkTrigger(controller, heavy); lara->checkTrigger(controller, heavy);
} }
virtual int addSprite(TR::Entity::Type type, int room, int x, int y, int z, int frame = -1, bool empty = false) {
return Sprite::add(this, type, room, x, y, z, frame, empty);
}
virtual int addEnemy(TR::Entity::Type type, int room, const vec3 &pos, float angle) {
int index = level.entityAdd(type, room, int(pos.x), int(pos.y), int(pos.z), TR::angle(angle), -1);
if (index > -1) {
TR::Entity &e = level.entities[index];
Enemy *enemy = NULL;
switch (type) {
case TR::Entity::ENEMY_MUTANT_1 :
case TR::Entity::ENEMY_MUTANT_2 :
case TR::Entity::ENEMY_MUTANT_3 :
enemy = new Mutant(this, index);
break;
case TR::Entity::ENEMY_CENTAUR :
enemy = new Centaur(this, index);
break;
case TR::Entity::ENEMY_GIANT_MUTANT :
enemy = new GiantMutant(this, index);
break;
default : ;
}
ASSERT(enemy);
e.controller = enemy;
e.flags.active = TR::ACTIVE;
enemy->activate();
}
return index;
}
virtual bool invUse(TR::Entity::Type type) { virtual bool invUse(TR::Entity::Type type) {
if (!lara->useItem(type)) if (!lara->useItem(type))
return inventory.use(type); return inventory.use(type);
@@ -338,15 +371,14 @@ struct Level : IGame {
case TR::Entity::ENEMY_BAT : case TR::Entity::ENEMY_BAT :
entity.controller = new Bat(this, i); entity.controller = new Bat(this, i);
break; break;
case TR::Entity::ENEMY_TWIN :
case TR::Entity::ENEMY_CROCODILE_LAND :
case TR::Entity::ENEMY_CROCODILE_WATER :
case TR::Entity::ENEMY_LION_MALE : case TR::Entity::ENEMY_LION_MALE :
case TR::Entity::ENEMY_LION_FEMALE : case TR::Entity::ENEMY_LION_FEMALE :
case TR::Entity::ENEMY_PUMA : entity.controller = new Lion(this, i);
case TR::Entity::ENEMY_GORILLA : break;
case TR::Entity::ENEMY_RAT_LAND : case TR::Entity::ENEMY_RAT_LAND :
case TR::Entity::ENEMY_RAT_WATER : case TR::Entity::ENEMY_RAT_WATER :
entity.controller = new Rat(this, i);
break;
case TR::Entity::ENEMY_REX : case TR::Entity::ENEMY_REX :
entity.controller = new Rex(this, i); entity.controller = new Rex(this, i);
break; break;
@@ -356,9 +388,23 @@ struct Level : IGame {
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 :
entity.controller = new Mutant(this, i);
break;
case TR::Entity::ENEMY_CENTAUR : case TR::Entity::ENEMY_CENTAUR :
entity.controller = new Centaur(this, i);
break;
case TR::Entity::ENEMY_MUMMY : case TR::Entity::ENEMY_MUMMY :
case TR::Entity::ENEMY_TWIN :
case TR::Entity::ENEMY_CROCODILE_LAND :
case TR::Entity::ENEMY_CROCODILE_WATER :
case TR::Entity::ENEMY_PUMA :
case TR::Entity::ENEMY_GORILLA :
case TR::Entity::ENEMY_LARSON : case TR::Entity::ENEMY_LARSON :
case TR::Entity::ENEMY_PIERRE :
case TR::Entity::ENEMY_SKATEBOY :
case TR::Entity::ENEMY_COWBOY :
case TR::Entity::ENEMY_MR_T :
case TR::Entity::ENEMY_NATLA :
entity.controller = new Enemy(this, i, 100, 10, 0.0f, 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 :
@@ -454,6 +500,10 @@ struct Level : IGame {
case TR::Entity::BOAT : case TR::Entity::BOAT :
entity.controller = new Boat(this, i); entity.controller = new Boat(this, i);
break; break;
case TR::Entity::MUTANT_EGG_SMALL :
case TR::Entity::MUTANT_EGG_BIG :
entity.controller = new MutantEgg(this, i);
break;
default : default :
if (entity.modelIndex > 0) if (entity.modelIndex > 0)
entity.controller = new Controller(this, i); entity.controller = new Controller(this, i);

View File

@@ -784,6 +784,44 @@ struct Boat : Controller {
}; };
#define MUTANT_EGG_RANGE 4096
struct MutantEgg : Controller {
enum {
STATE_IDLE,
STATE_EXPLOSION,
};
TR::Entity::Type enemy;
MutantEgg(IGame *game, int entity) : Controller(game, entity) {
initMeshOverrides();
layers[0].mask = 0xff0001ff; // hide dynamic meshes
switch (getEntity().flags.active) {
case 1 : enemy = TR::Entity::ENEMY_MUTANT_2; break;
case 2 : enemy = TR::Entity::ENEMY_CENTAUR; break;
case 4 : enemy = TR::Entity::ENEMY_GIANT_MUTANT; break;
case 8 : enemy = TR::Entity::ENEMY_MUTANT_3; break;
default : enemy = TR::Entity::ENEMY_MUTANT_1;
}
}
virtual void update() {
if (state != STATE_EXPLOSION) {
Box box = Box(pos + vec3(-MUTANT_EGG_RANGE), pos + vec3(MUTANT_EGG_RANGE));
TR::Entity &e = getEntity();
if ( e.flags.once || e.type == TR::Entity::MUTANT_EGG_BIG || box.contains((((Controller*)level->laraController)->pos)) ) {
animation.setState(STATE_EXPLOSION);
layers[0].mask = 0xffffffff & ~(1 << 24);
explode(0x00fffe00);
game->addEnemy(enemy, getRoomIndex(), pos, angle.y);
}
}
Controller::update();
}
};
struct KeyHole : Controller { struct KeyHole : Controller {
KeyHole(IGame *game, int entity) : Controller(game, entity) {} KeyHole(IGame *game, int entity) : Controller(game, entity) {}

View File

@@ -62,11 +62,21 @@ inline const T& min(const T &a, const T &b) {
return a < b ? a : b; return a < b ? a : b;
} }
template <typename T>
inline const T& min(const T &a, const T &b, const T &c) {
return (a < b && a < c) ? a : ((b < a && b < c) ? b : c);
}
template <class T> template <class T>
inline const T& max(const T &a, const T &b) { inline const T& max(const T &a, const T &b) {
return a > b ? a : b; return a > b ? a : b;
} }
template <typename T>
inline const T& max(const T &a, const T &b, const T &c) {
return (a > b && a > c) ? a : ((b > a && b > c) ? b : c);
}
template <class T> template <class T>
inline const T& clamp(const T &x, const T &a, const T &b) { inline const T& clamp(const T &x, const T &a, const T &b) {
return x < a ? a : (x > b ? b : x); return x < a ? a : (x > b ? b : x);