mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-13 16:44:50 +02:00
This commit is contained in:
@@ -31,7 +31,6 @@ struct Camera : ICamera {
|
||||
int room;
|
||||
|
||||
float timer;
|
||||
float shake;
|
||||
|
||||
Basis prevBasis;
|
||||
|
||||
@@ -43,7 +42,7 @@ struct Camera : ICamera {
|
||||
bool firstPerson;
|
||||
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);
|
||||
if (owner->getEntity().type != TR::Entity::LARA && level->cameraFrames) {
|
||||
state = STATE_CUTSCENE;
|
||||
|
100
src/controller.h
100
src/controller.h
@@ -17,10 +17,11 @@
|
||||
struct Controller;
|
||||
|
||||
struct ICamera {
|
||||
vec4 *reflectPlane;
|
||||
vec3 pos;
|
||||
vec4 *reflectPlane;
|
||||
vec3 pos;
|
||||
float shake;
|
||||
|
||||
ICamera() : reflectPlane(NULL) {}
|
||||
ICamera() : reflectPlane(NULL), pos(0.0f), shake(0.0f) {}
|
||||
|
||||
virtual void setup(bool calcMatrices) {}
|
||||
virtual int getRoomIndex() const { return TR::NO_ROOM; }
|
||||
@@ -52,6 +53,9 @@ struct IGame {
|
||||
|
||||
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 void invAdd(TR::Entity::Type type, int count = 1) {}
|
||||
virtual int* invCount(TR::Entity::Type type) { return NULL; }
|
||||
@@ -65,7 +69,7 @@ struct IGame {
|
||||
struct Controller {
|
||||
static Controller *first;
|
||||
Controller *next;
|
||||
enum ActiveState { asNone, asActive, asInactive } activeState;
|
||||
enum ActiveState { asNone, asActive, asInactive };
|
||||
|
||||
IGame *game;
|
||||
TR::Level *level;
|
||||
@@ -94,7 +98,17 @@ struct Controller {
|
||||
uint32 mask;
|
||||
} *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();
|
||||
pos = vec3(float(e.x), float(e.y), float(e.z));
|
||||
angle = vec3(0.0f, e.rotation, 0.0f);
|
||||
@@ -125,6 +139,7 @@ struct Controller {
|
||||
virtual ~Controller() {
|
||||
delete[] joints;
|
||||
delete[] layers;
|
||||
delete[] explodeParts;
|
||||
deactivate(true);
|
||||
}
|
||||
|
||||
@@ -195,6 +210,7 @@ struct Controller {
|
||||
}
|
||||
|
||||
void initMeshOverrides() {
|
||||
if (layers) return;
|
||||
layers = new MeshLayer[MAX_LAYERS];
|
||||
memset(layers, 0, sizeof(MeshLayer) * MAX_LAYERS);
|
||||
layers[0].model = getEntity().modelIndex - 1;
|
||||
@@ -223,6 +239,8 @@ struct Controller {
|
||||
|
||||
Basis b = animation.getJoints(Basis(getMatrix()), joint);
|
||||
vec3 delta = (b.inverse() * t).normal();
|
||||
if (invertAim)
|
||||
delta = -delta;
|
||||
|
||||
float angleY = clampAngle(atan2f(delta.x, delta.z));
|
||||
float angleX = clampAngle(asinf(delta.y));
|
||||
@@ -526,8 +544,37 @@ struct Controller {
|
||||
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() {
|
||||
updateAnimation(true);
|
||||
updateExplosion();
|
||||
}
|
||||
|
||||
void updateLights(bool lerp = true) {
|
||||
@@ -587,6 +634,34 @@ struct Controller {
|
||||
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) {
|
||||
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(uAmbient, vec3(0.0f));
|
||||
|
||||
Core::setDepthWrite(false);
|
||||
Core::setBlending(bmMultiply);
|
||||
mesh->renderShadowBlob();
|
||||
Core::setBlending(bmNone);
|
||||
Core::setDepthWrite(true);
|
||||
}
|
||||
|
||||
virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { // TODO: animation.calcJoints
|
||||
mat4 matrix = getMatrix();
|
||||
|
||||
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;
|
||||
|
||||
TR::Entity &entity = getEntity();
|
||||
@@ -643,16 +720,23 @@ struct Controller {
|
||||
animation.getJoints(matrix, -1, true, joints);
|
||||
|
||||
if (layers) {
|
||||
int mask = 0;
|
||||
uint32 mask = 0;
|
||||
|
||||
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;
|
||||
mask |= layers[i].mask;
|
||||
// set meshes visibility
|
||||
for (int j = 0; j < model->mCount; j++)
|
||||
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)
|
||||
// joints[14].w = -1.0f;
|
||||
// render
|
||||
|
157
src/enemy.h
157
src/enemy.h
@@ -57,6 +57,7 @@ struct Enemy : Character {
|
||||
float length; // dist from center to head (jaws)
|
||||
float aggression;
|
||||
int radius;
|
||||
int hitSound;
|
||||
|
||||
Character *target;
|
||||
Path *path;
|
||||
@@ -85,7 +86,13 @@ struct Enemy : Character {
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -126,7 +133,7 @@ struct Enemy : Character {
|
||||
|
||||
if (px != nx) pos.x = float(nx);
|
||||
if (pz != nz) pos.z = float(nz);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void updatePosition() {
|
||||
if (!getEntity().flags.active) return;
|
||||
@@ -137,7 +144,11 @@ struct Enemy : Character {
|
||||
clipByBox(pos);
|
||||
|
||||
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) {
|
||||
switch (stand) {
|
||||
@@ -221,6 +232,8 @@ struct Enemy : Character {
|
||||
virtual void hit(float damage, Controller *enemy = NULL, TR::HitType hitType = TR::HIT_DEFAULT) {
|
||||
Character::hit(damage, enemy, hitType);
|
||||
wound = true;
|
||||
if (hitSound > -1)
|
||||
game->playSound(hitSound, pos, Sound::PAN);
|
||||
};
|
||||
|
||||
void bite(const vec3 &pos, float damage) {
|
||||
@@ -486,6 +499,7 @@ struct Wolf : Enemy {
|
||||
dropHeight = -1024;
|
||||
jointChest = 2;
|
||||
jointHead = 3;
|
||||
hitSound = TR::SND_HIT_WOLF;
|
||||
nextState = STATE_NONE;
|
||||
animation.time = animation.timeMax;
|
||||
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_HOWL 2048
|
||||
#define BEAR_DIST_BITE 1024
|
||||
@@ -641,6 +667,7 @@ struct Bear : Enemy {
|
||||
Bear(IGame *game, int entity) : Enemy(game, entity, 20, 341, 500.0f, 0.5f) {
|
||||
jointChest = 13;
|
||||
jointHead = 14;
|
||||
hitSound = TR::SND_HIT_BEAR;
|
||||
nextState = STATE_NONE;
|
||||
}
|
||||
|
||||
@@ -814,13 +841,6 @@ struct Bat : Enemy {
|
||||
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() {
|
||||
float angleY = 0.0f;
|
||||
if (state == STATE_FLY || state == STATE_ATTACK)
|
||||
@@ -1065,4 +1085,121 @@ struct Raptor : Enemy {
|
||||
}
|
||||
};
|
||||
|
||||
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
|
27
src/format.h
27
src/format.h
@@ -254,6 +254,9 @@ namespace TR {
|
||||
SND_SHOTGUN_RELOAD = 9,
|
||||
SND_RICOCHET = 10,
|
||||
|
||||
SND_HIT_BEAR = 16,
|
||||
SND_HIT_WOLF = 20,
|
||||
|
||||
SND_SCREAM = 30,
|
||||
SND_HIT = 31,
|
||||
|
||||
@@ -267,6 +270,12 @@ namespace TR {
|
||||
|
||||
SND_FLOOD = 81,
|
||||
|
||||
SND_HIT_LION = 85,
|
||||
|
||||
SND_HIT_RAT = 95,
|
||||
|
||||
SND_EXPLOSION = 104,
|
||||
|
||||
SND_INV_SPIN = 108,
|
||||
SND_INV_HOME = 109,
|
||||
SND_INV_CONTROLS = 110,
|
||||
@@ -278,9 +287,15 @@ namespace TR {
|
||||
SND_HEALTH = 116,
|
||||
|
||||
SND_STAIRS2SLOPE = 119,
|
||||
|
||||
SND_HIT_SKATEBOY = 132,
|
||||
|
||||
SND_HIT_MUTANT = 142,
|
||||
|
||||
SND_DART = 151,
|
||||
|
||||
SND_EXPLOSION = 170,
|
||||
SND_TNT = 170,
|
||||
SND_MUTANT_DEATH = 171,
|
||||
SND_SECRET = 173,
|
||||
};
|
||||
|
||||
@@ -730,7 +745,10 @@ namespace TR {
|
||||
&& type != ENEMY_REX
|
||||
&& type != ENEMY_RAPTOR
|
||||
&& type != ENEMY_MUTANT_1
|
||||
&& type != ENEMY_MUTANT_2
|
||||
&& type != ENEMY_MUTANT_3
|
||||
&& type != ENEMY_CENTAUR
|
||||
&& type != ENEMY_GIANT_MUTANT
|
||||
&& type != ENEMY_MUMMY
|
||||
&& type != ENEMY_NATLA)
|
||||
opaque = true;
|
||||
@@ -2016,6 +2034,9 @@ namespace TR {
|
||||
}
|
||||
|
||||
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++)
|
||||
if (type == models[i].type)
|
||||
return i + 1;
|
||||
@@ -2107,8 +2128,8 @@ namespace TR {
|
||||
int sz = (z - room.info.z) >> 10;
|
||||
|
||||
if (sz <= 0 || sz >= room.xSectors - 1) {
|
||||
sx = clamp(sx, 1, room.xSectors - 2);
|
||||
sz = clamp(sz, 0, room.zSectors - 1);
|
||||
sx = clamp(sx, 0, room.xSectors - 1);
|
||||
sz = clamp(sz, 1, room.zSectors - 2);
|
||||
} else
|
||||
sx = clamp(sx, 0, room.xSectors - 1);
|
||||
|
||||
|
@@ -180,8 +180,17 @@ struct Inventory {
|
||||
// add(TR::Entity::INV_MEDIKIT_SMALL, 999);
|
||||
// add(TR::Entity::INV_MEDIKIT_BIG, 999);
|
||||
// add(TR::Entity::INV_SCION, 1);
|
||||
// add(TR::Entity::INV_KEY_1, 1);
|
||||
// add(TR::Entity::INV_PUZZLE_1, 1);
|
||||
#ifdef _DEBUG
|
||||
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) {
|
||||
|
62
src/level.h
62
src/level.h
@@ -209,9 +209,10 @@ struct Level : IGame {
|
||||
playSound(TR::SND_STAIRS2SLOPE, vec3(), 0);
|
||||
break;
|
||||
case TR::Effect::EXPLOSION :
|
||||
playSound(TR::SND_EXPLOSION, vec3(0), 0);
|
||||
playSound(TR::SND_TNT, vec3(0), 0);
|
||||
camera->shake = 1.0f;
|
||||
break;
|
||||
default : ;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,6 +220,38 @@ struct Level : IGame {
|
||||
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) {
|
||||
if (!lara->useItem(type))
|
||||
return inventory.use(type);
|
||||
@@ -338,15 +371,14 @@ struct Level : IGame {
|
||||
case TR::Entity::ENEMY_BAT :
|
||||
entity.controller = new Bat(this, i);
|
||||
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_FEMALE :
|
||||
case TR::Entity::ENEMY_PUMA :
|
||||
case TR::Entity::ENEMY_GORILLA :
|
||||
entity.controller = new Lion(this, i);
|
||||
break;
|
||||
case TR::Entity::ENEMY_RAT_LAND :
|
||||
case TR::Entity::ENEMY_RAT_WATER :
|
||||
entity.controller = new Rat(this, i);
|
||||
break;
|
||||
case TR::Entity::ENEMY_REX :
|
||||
entity.controller = new Rex(this, i);
|
||||
break;
|
||||
@@ -356,9 +388,23 @@ struct Level : IGame {
|
||||
case TR::Entity::ENEMY_MUTANT_1 :
|
||||
case TR::Entity::ENEMY_MUTANT_2 :
|
||||
case TR::Entity::ENEMY_MUTANT_3 :
|
||||
entity.controller = new Mutant(this, i);
|
||||
break;
|
||||
case TR::Entity::ENEMY_CENTAUR :
|
||||
entity.controller = new Centaur(this, i);
|
||||
break;
|
||||
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_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);
|
||||
break;
|
||||
case TR::Entity::DOOR_1 :
|
||||
@@ -454,6 +500,10 @@ struct Level : IGame {
|
||||
case TR::Entity::BOAT :
|
||||
entity.controller = new Boat(this, i);
|
||||
break;
|
||||
case TR::Entity::MUTANT_EGG_SMALL :
|
||||
case TR::Entity::MUTANT_EGG_BIG :
|
||||
entity.controller = new MutantEgg(this, i);
|
||||
break;
|
||||
default :
|
||||
if (entity.modelIndex > 0)
|
||||
entity.controller = new Controller(this, i);
|
||||
|
@@ -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 {
|
||||
KeyHole(IGame *game, int entity) : Controller(game, entity) {}
|
||||
|
||||
|
10
src/utils.h
10
src/utils.h
@@ -62,11 +62,21 @@ inline const T& min(const T &a, const T &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>
|
||||
inline const T& max(const T &a, const T &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>
|
||||
inline const T& clamp(const T &x, const T &a, const T &b) {
|
||||
return x < a ? a : (x > b ? b : x);
|
||||
|
Reference in New Issue
Block a user