1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-17 10:30:47 +02:00

#22 fix dart emitters with timers, add scion & earthquake entity for LEVEL10C; #14 disable activation for dead enemies, fix hit sounds, add mummy entity for LEVEL3B

This commit is contained in:
XProger
2017-10-02 07:09:44 +03:00
parent fab5d8b301
commit c208ece45c
6 changed files with 265 additions and 233 deletions

View File

@@ -363,7 +363,7 @@ struct Controller {
void getSpheres(Sphere *spheres, int &count) { void getSpheres(Sphere *spheres, int &count) {
TR::Model *m = getModel(); TR::Model *m = getModel();
Basis basis(getMatrix()); Basis basis(getMatrix());
// TODO: optimize (check frame index for animation updates, use joints array)
count = 0; count = 0;
for (int i = 0; i < m->mCount; i++) { for (int i = 0; i < m->mCount; i++) {
TR::Mesh &aMesh = level->meshes[level->meshOffsets[m->mStart + i]]; TR::Mesh &aMesh = level->meshes[level->meshOffsets[m->mStart + i]];
@@ -556,9 +556,36 @@ struct Controller {
part.basis = Basis(part.basis.rot * q, part.basis.pos + explodeParts[i].velocity * (Core::deltaTime * 30.0f)); part.basis = Basis(part.basis.rot * q, part.basis.pos + explodeParts[i].velocity * (Core::deltaTime * 30.0f));
vec3 p = part.basis.pos; vec3 p = part.basis.pos;
TR::Room::Sector *s = level->getSector(part.roomIndex, int(p.x), int(p.y), int(p.z)); //TR::Room::Sector *s = level->getSector(part.roomIndex, int(p.x), int(p.y), int(p.z));
TR::Level::FloorInfo info;
level->getFloorInfo(part.roomIndex, int(p.x), int(p.y), int(p.z), info);
if (s && s->floor * 256.0f < p.y) { if (info.roomNext != TR::NO_ROOM)
part.roomIndex = info.roomNext;
bool explode = false;
if (p.y < info.roomCeiling) {
if (info.roomAbove != TR::NO_ROOM)
part.roomIndex = info.roomAbove;
else {
if (info.roomCeiling != 0xffff8100)
p.y = (float)info.roomCeiling;
explode = true;
}
}
if (p.y > info.roomFloor) {
if (info.roomBelow != TR::NO_ROOM)
part.roomIndex = info.roomBelow;
else {
if (info.roomFloor != 0xffff8100)
p.y = (float)info.roomFloor;
explode = true;
}
}
if (explode) {
explodeMask &= ~(1 << i); explodeMask &= ~(1 << i);
game->addSprite(TR::Entity::EXPLOSION, part.roomIndex, int(p.x), int(p.y), int(p.z)); game->addSprite(TR::Entity::EXPLOSION, part.roomIndex, int(p.x), int(p.y), int(p.z));

View File

@@ -68,7 +68,7 @@ struct Enemy : Character {
bool targetFromView; // enemy in target view zone bool targetFromView; // enemy in target view zone
bool targetCanAttack; bool targetCanAttack;
Enemy(IGame *game, int entity, float 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) { Enemy(IGame *game, int entity, float 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), hitSound(-1), target(NULL), path(NULL) {
targetDist = +INF; targetDist = +INF;
targetInView = targetFromView = targetCanAttack = false; targetInView = targetFromView = targetCanAttack = false;
} }
@@ -78,7 +78,7 @@ struct Enemy : Character {
} }
virtual bool activate() { virtual bool activate() {
if (Character::activate()) { if (health > 0.0f && Character::activate()) {
target = (Character*)game->getLara(); target = (Character*)game->getLara();
return true; return true;
} }
@@ -1202,4 +1202,76 @@ struct Centaur : Enemy {
}; };
struct Mummy : Enemy {
enum {
STATE_NONE,
STATE_IDLE,
STATE_FALL,
};
Mummy(IGame *game, int entity) : Enemy(game, entity, 18, 341, 150.0f, 0.0f) {
jointHead = 2;
}
virtual void update() {
if (state == STATE_IDLE && (health <= 0.0f || collide((Controller*)level->laraController))) {
animation.setState(STATE_FALL);
health = 0.0f;
}
Enemy::update();
}
virtual int getStateGround() {
if (!think(true))
return state;
return state;
}
virtual void updatePosition() {
Enemy::updatePosition();
setOverrides(true, jointChest, jointHead);
lookAt(target);
}
};
struct Doppelganger : Enemy {
Doppelganger(IGame *game, int entity) : Enemy(game, entity, 1000, 341, 150.0f, 0.0f) {
jointChest = 1;
jointHead = 2;
}
virtual void update() {
Enemy::update();
}
virtual int getStateGround() {
if (!think(true))
return state;
return state;
}
virtual void updatePosition() {
Enemy::updatePosition();
setOverrides(true, jointChest, jointHead);
lookAt(target);
}
};
struct ScionTarget : Enemy {
ScionTarget(IGame *game, int entity) : Enemy(game, entity, 5, 0, 0, 0) {}
virtual void update() {
Controller::update();
if (health <= 0.0f) {
getEntity().flags.invisible = true;
game->checkTrigger(this, true);
deactivate();
}
}
};
#endif #endif

View File

@@ -17,7 +17,7 @@
E( LARA_MAGNUMS ) \ E( LARA_MAGNUMS ) \
E( LARA_UZIS ) \ E( LARA_UZIS ) \
E( LARA_SPEC ) \ E( LARA_SPEC ) \
E( ENEMY_TWIN ) \ E( ENEMY_DOPPELGANGER ) \
E( ENEMY_WOLF ) \ E( ENEMY_WOLF ) \
E( ENEMY_BEAR ) \ E( ENEMY_BEAR ) \
E( ENEMY_BAT ) \ E( ENEMY_BAT ) \
@@ -154,10 +154,10 @@
E( KEY_HOLE_4 ) \ E( KEY_HOLE_4 ) \
E( UNUSED_4 ) \ E( UNUSED_4 ) \
E( UNUSED_5 ) \ E( UNUSED_5 ) \
E( SCION_1 ) \ E( SCION_QUALOPEC ) \
E( SCION_2 ) \ E( SCION_DROP ) \
E( SCION_3 ) \
E( SCION_TARGET ) \ E( SCION_TARGET ) \
E( SCION_NATLA ) \
E( SCION_HOLDER ) \ E( SCION_HOLDER ) \
E( UNUSED_6 ) \ E( UNUSED_6 ) \
E( UNUSED_7 ) \ E( UNUSED_7 ) \
@@ -647,7 +647,7 @@ namespace TR {
void *controller; // Controller implementation or NULL void *controller; // Controller implementation or NULL
bool isEnemy() const { bool isEnemy() const {
return type >= ENEMY_TWIN && type <= ENEMY_GIANT_MUTANT; return (type >= ENEMY_DOPPELGANGER && type <= ENEMY_GIANT_MUTANT) || type == SCION_TARGET;
} }
bool isBigEnemy() const { bool isBigEnemy() const {
@@ -669,7 +669,7 @@ namespace TR {
return (type >= PISTOLS && type <= AMMO_UZIS) || return (type >= PISTOLS && type <= AMMO_UZIS) ||
(type >= PUZZLE_1 && type <= PUZZLE_4) || (type >= PUZZLE_1 && type <= PUZZLE_4) ||
(type >= KEY_ITEM_1 && type <= KEY_ITEM_4) || (type >= KEY_ITEM_1 && type <= KEY_ITEM_4) ||
(type == MEDIKIT_SMALL || type == MEDIKIT_BIG || type == SCION_1 || type == LEADBAR); // TODO: recheck all items (type == MEDIKIT_SMALL || type == MEDIKIT_BIG || type == SCION_QUALOPEC || type == SCION_DROP || type == SCION_NATLA || type == LEADBAR); // TODO: recheck all items
} }
bool isActor() const { bool isActor() const {

View File

@@ -426,17 +426,14 @@ struct Lara : Character {
if (level->extra.braid > -1) if (level->extra.braid > -1)
braid = new Braid(this, vec3(-4.0f, 24.0f, -48.0f)); braid = new Braid(this, vec3(-4.0f, 24.0f, -48.0f));
//reset(19, vec3(41418, -3707, 58863), 270 * DEG2RAD); // level 5 (triangle)
//reset(9, vec3(63008, 0, 37787), 0); // level 2 (switch)
//reset(5, vec3(38643, -3072, 92370), PI * 0.5f); // level 3a (gears)
//reset(15, vec3(70067, -256, 29104), -0.68f); // level 2 (pool)
//reset(26, vec3(24475, 6912, 83505), 90 * DEG2RAD); // level 1 (switch timer)
#ifdef _DEBUG #ifdef _DEBUG
//reset(14, vec3(40448, 3584, 60928), PI * 0.5f, STAND_ONWATER); // gym (pool) //reset(14, vec3(40448, 3584, 60928), PI * 0.5f, STAND_ONWATER); // gym (pool)
//reset(0, vec3(74858, 3072, 20795), 0); // level 1 (dart) //reset(0, vec3(74858, 3072, 20795), 0); // level 1 (dart)
//reset(14, vec3(20215, 6656, 52942), PI); // level 1 (bridge) //reset(14, vec3(20215, 6656, 52942), PI); // level 1 (bridge)
//reset(20, vec3(8952, 3840, 68071), PI); // level 1 (crystal) //reset(20, vec3(8952, 3840, 68071), PI); // level 1 (crystal)
//reset(26, vec3(24475, 6912, 83505), 90 * DEG2RAD); // level 1 (switch timer)
//reset(33, vec3(48229, 4608, 78420), 270 * DEG2RAD); // level 1 (end) //reset(33, vec3(48229, 4608, 78420), 270 * DEG2RAD); // level 1 (end)
//reset(9, vec3(63008, 0, 37787), 0); // level 2 (switch)
//reset(15, vec3(70067, -256, 29104), -0.68f); // level 2 (pool) //reset(15, vec3(70067, -256, 29104), -0.68f); // level 2 (pool)
//reset(26, vec3(71980, 1546, 19000), 270 * DEG2RAD); // level 2 (underwater switch) //reset(26, vec3(71980, 1546, 19000), 270 * DEG2RAD); // level 2 (underwater switch)
//reset(61, vec3(27221, -1024, 29205), -PI * 0.5f); // level 2 (blade) //reset(61, vec3(27221, -1024, 29205), -PI * 0.5f); // level 2 (blade)
@@ -457,6 +454,7 @@ struct Lara : Character {
//reset(18, vec3(34914, 11008, 41315), 90 * DEG2RAD); // level 4 (main hall) //reset(18, vec3(34914, 11008, 41315), 90 * DEG2RAD); // level 4 (main hall)
//reset(19, vec3(33368, 19968, 45643), 270 * DEG2RAD); // level 4 (damocles) //reset(19, vec3(33368, 19968, 45643), 270 * DEG2RAD); // level 4 (damocles)
//reset(24, vec3(45609, 18176, 41500), 90 * DEG2RAD); // level 4 (thor) //reset(24, vec3(45609, 18176, 41500), 90 * DEG2RAD); // level 4 (thor)
//reset(19, vec3(41418, -3707, 58863), 270 * DEG2RAD); // level 5 (triangle)
//reset(21, vec3(24106, -4352, 52089), 0); // level 6 (flame traps) //reset(21, vec3(24106, -4352, 52089), 0); // level 6 (flame traps)
//reset(73, vec3(73372, -122, 51687), PI * 0.5f); // level 6 (midas hand) //reset(73, vec3(73372, -122, 51687), PI * 0.5f); // level 6 (midas hand)
//reset(64, vec3(36839, -2560, 48769), 270 * DEG2RAD); // level 6 (flipmap effect) //reset(64, vec3(36839, -2560, 48769), 270 * DEG2RAD); // level 6 (flipmap effect)
@@ -842,6 +840,7 @@ struct Lara : Character {
if (arm->target && checkHit(arm->target, p, hit, hit)) { if (arm->target && checkHit(arm->target, p, hit, hit)) {
((Character*)arm->target)->hit(wpnGetDamage()); ((Character*)arm->target)->hit(wpnGetDamage());
hit -= d * 64.0f; hit -= d * 64.0f;
if (arm->target->getEntity().type != TR::Entity::SCION_TARGET)
Sprite::add(game, TR::Entity::BLOOD, room, (int)hit.x, (int)hit.y, (int)hit.z, Sprite::FRAME_ANIMATED); Sprite::add(game, TR::Entity::BLOOD, room, (int)hit.x, (int)hit.y, (int)hit.z, Sprite::FRAME_ANIMATED);
} else { } else {
hit -= d * 64.0f; hit -= d * 64.0f;
@@ -1494,7 +1493,7 @@ struct Lara : Character {
controller->angle.x = -25 * DEG2RAD; controller->angle.x = -25 * DEG2RAD;
controller->angle.y = angle.y; controller->angle.y = angle.y;
if (item.type == TR::Entity::SCION_1) if (item.type == TR::Entity::SCION_QUALOPEC)
limit = TR::Limits::SCION; limit = TR::Limits::SCION;
if (!checkInteraction(controller, limit, (input & ACTION) != 0)) if (!checkInteraction(controller, limit, (input & ACTION) != 0))
@@ -1505,7 +1504,7 @@ struct Lara : Character {
pickupEntity = &item; pickupEntity = &item;
if (item.type == TR::Entity::SCION_1) { if (item.type == TR::Entity::SCION_QUALOPEC) {
animation.setAnim(level->models[TR::MODEL_LARA_SPEC].animation); animation.setAnim(level->models[TR::MODEL_LARA_SPEC].animation);
((Camera*)level->cameraController)->state = Camera::STATE_CUTSCENE; ((Camera*)level->cameraController)->state = Camera::STATE_CUTSCENE;
level->cutMatrix.identity(); level->cutMatrix.identity();

View File

@@ -225,23 +225,7 @@ struct Level : IGame {
int index = level.entityAdd(type, room, int(pos.x), int(pos.y), int(pos.z), TR::angle(angle), -1); int index = level.entityAdd(type, room, int(pos.x), int(pos.y), int(pos.z), TR::angle(angle), -1);
if (index > -1) { if (index > -1) {
TR::Entity &e = level.entities[index]; TR::Entity &e = level.entities[index];
Enemy *enemy = NULL; Controller *enemy = initController(index);
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.controller = enemy;
e.flags.active = TR::ACTIVE; e.flags.active = TR::ACTIVE;
enemy->activate(); enemy->activate();
@@ -357,163 +341,10 @@ struct Level : IGame {
initOverrides(); initOverrides();
for (int i = 0; i < level.entitiesBaseCount; i++) { for (int i = 0; i < level.entitiesBaseCount; i++) {
TR::Entity &entity = level.entities[i]; TR::Entity &e = level.entities[i];
switch (entity.type) { e.controller = initController(i);
case TR::Entity::LARA : if (e.type == TR::Entity::LARA || e.type == TR::Entity::CUT_1)
case TR::Entity::CUT_1 : lara = (Lara*)e.controller;
entity.controller = (lara = new Lara(this, i));
break;
case TR::Entity::ENEMY_WOLF :
entity.controller = new Wolf(this, i);
break;
case TR::Entity::ENEMY_BEAR :
entity.controller = new Bear(this, i);
break;
case TR::Entity::ENEMY_BAT :
entity.controller = new Bat(this, i);
break;
case TR::Entity::ENEMY_LION_MALE :
case TR::Entity::ENEMY_LION_FEMALE :
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;
case TR::Entity::ENEMY_RAPTOR :
entity.controller = new Raptor(this, i);
break;
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 :
case TR::Entity::DOOR_2 :
case TR::Entity::DOOR_3 :
case TR::Entity::DOOR_4 :
case TR::Entity::DOOR_5 :
case TR::Entity::DOOR_6 :
case TR::Entity::DOOR_BIG_1 :
case TR::Entity::DOOR_BIG_2 :
entity.controller = new Door(this, i);
break;
case TR::Entity::TRAP_DOOR_1 :
case TR::Entity::TRAP_DOOR_2 :
entity.controller = new TrapDoor(this, i);
break;
case TR::Entity::BRIDGE_0 :
case TR::Entity::BRIDGE_1 :
case TR::Entity::BRIDGE_2 :
entity.controller = new Bridge(this, i);
break;
case TR::Entity::GEARS_1 :
case TR::Entity::GEARS_2 :
case TR::Entity::GEARS_3 :
entity.controller = new Gear(this, i);
break;
case TR::Entity::TRAP_FLOOR :
entity.controller = new TrapFloor(this, i);
break;
case TR::Entity::CRYSTAL :
entity.controller = new Crystal(this, i);
break;
case TR::Entity::TRAP_BLADE :
entity.controller = new TrapBlade(this, i);
break;
case TR::Entity::TRAP_SPIKES :
entity.controller = new TrapSpikes(this, i);
break;
case TR::Entity::TRAP_BOULDER :
entity.controller = new TrapBoulder(this, i);
break;
case TR::Entity::TRAP_DART_EMITTER :
entity.controller = new TrapDartEmitter(this, i);
break;
case TR::Entity::DRAWBRIDGE :
entity.controller = new Drawbridge(this, i);
break;
case TR::Entity::BLOCK_1 :
case TR::Entity::BLOCK_2 :
case TR::Entity::BLOCK_3 :
case TR::Entity::BLOCK_4 :
entity.controller = new Block(this, i);
break;
case TR::Entity::MOVING_BLOCK :
entity.controller = new MovingBlock(this, i);
break;
case TR::Entity::TRAP_CEILING_1 :
case TR::Entity::TRAP_CEILING_2 :
entity.controller = new TrapCeiling(this, i);
break;
case TR::Entity::TRAP_SLAM :
entity.controller = new TrapSlam(this, i);
break;
case TR::Entity::TRAP_SWORD :
entity.controller = new TrapSword(this, i);
break;
case TR::Entity::DOOR_LATCH :
entity.controller = new DoorLatch(this, i);
break;
case TR::Entity::SWITCH :
case TR::Entity::SWITCH_WATER :
entity.controller = new Switch(this, i);
break;
case TR::Entity::PUZZLE_HOLE_1 :
case TR::Entity::PUZZLE_HOLE_2 :
case TR::Entity::PUZZLE_HOLE_3 :
case TR::Entity::PUZZLE_HOLE_4 :
case TR::Entity::KEY_HOLE_1 :
case TR::Entity::KEY_HOLE_2 :
case TR::Entity::KEY_HOLE_3 :
case TR::Entity::KEY_HOLE_4 :
entity.controller = new KeyHole(this, i);
break;
case TR::Entity::WATERFALL :
entity.controller = new Waterfall(this, i);
break;
case TR::Entity::TRAP_LAVA :
entity.controller = new TrapLava(this, i);
break;
case TR::Entity::CABIN :
entity.controller = new Cabin(this, i);
break;
case TR::Entity::TRAP_FLAME_EMITTER :
entity.controller = new TrapFlameEmitter(this, i);
break;
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);
else
entity.controller = new Sprite(this, i, 0);
}
} }
if (level.id != TR::TITLE) { if (level.id != TR::TITLE) {
@@ -583,6 +414,92 @@ struct Level : IGame {
Sound::stopAll(); Sound::stopAll();
} }
Controller* initController(int index) {
switch (level.entities[index].type) {
case TR::Entity::LARA :
case TR::Entity::CUT_1 : return new Lara(this, index);
case TR::Entity::ENEMY_DOPPELGANGER : return new Doppelganger(this, index);
case TR::Entity::ENEMY_WOLF : return new Wolf(this, index);
case TR::Entity::ENEMY_BEAR : return new Bear(this, index);
case TR::Entity::ENEMY_BAT : return new Bat(this, index);
case TR::Entity::ENEMY_LION_MALE :
case TR::Entity::ENEMY_LION_FEMALE : return new Lion(this, index);
case TR::Entity::ENEMY_RAT_LAND :
case TR::Entity::ENEMY_RAT_WATER : return new Rat(this, index);
case TR::Entity::ENEMY_REX : return new Rex(this, index);
case TR::Entity::ENEMY_RAPTOR : return new Raptor(this, index);
case TR::Entity::ENEMY_MUTANT_1 :
case TR::Entity::ENEMY_MUTANT_2 :
case TR::Entity::ENEMY_MUTANT_3 : return new Mutant(this, index);
case TR::Entity::ENEMY_CENTAUR : return new Centaur(this, index);
case TR::Entity::ENEMY_MUMMY : return new Mummy(this, index);
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 : return new Enemy(this, index, 100, 10, 0.0f, 0.0f);
case TR::Entity::ENEMY_GIANT_MUTANT : return new GiantMutant(this, index);
case TR::Entity::DOOR_1 :
case TR::Entity::DOOR_2 :
case TR::Entity::DOOR_3 :
case TR::Entity::DOOR_4 :
case TR::Entity::DOOR_5 :
case TR::Entity::DOOR_6 :
case TR::Entity::DOOR_BIG_1 :
case TR::Entity::DOOR_BIG_2 : return new Door(this, index);
case TR::Entity::TRAP_DOOR_1 :
case TR::Entity::TRAP_DOOR_2 : return new TrapDoor(this, index);
case TR::Entity::BRIDGE_0 :
case TR::Entity::BRIDGE_1 :
case TR::Entity::BRIDGE_2 : return new Bridge(this, index);
case TR::Entity::GEARS_1 :
case TR::Entity::GEARS_2 :
case TR::Entity::GEARS_3 : return new Gear(this, index);
case TR::Entity::TRAP_FLOOR : return new TrapFloor(this, index);
case TR::Entity::CRYSTAL : return new Crystal(this, index);
case TR::Entity::TRAP_BLADE : return new TrapBlade(this, index);
case TR::Entity::TRAP_SPIKES : return new TrapSpikes(this, index);
case TR::Entity::TRAP_BOULDER : return new TrapBoulder(this, index);
case TR::Entity::TRAP_DART_EMITTER : return new TrapDartEmitter(this, index);
case TR::Entity::DRAWBRIDGE : return new Drawbridge(this, index);
case TR::Entity::BLOCK_1 :
case TR::Entity::BLOCK_2 :
case TR::Entity::BLOCK_3 :
case TR::Entity::BLOCK_4 : return new Block(this, index);
case TR::Entity::MOVING_BLOCK : return new MovingBlock(this, index);
case TR::Entity::TRAP_CEILING_1 :
case TR::Entity::TRAP_CEILING_2 : return new TrapCeiling(this, index);
case TR::Entity::TRAP_SLAM : return new TrapSlam(this, index);
case TR::Entity::TRAP_SWORD : return new TrapSword(this, index);
case TR::Entity::DOOR_LATCH : return new DoorLatch(this, index);
case TR::Entity::SWITCH :
case TR::Entity::SWITCH_WATER : return new Switch(this, index);
case TR::Entity::PUZZLE_HOLE_1 :
case TR::Entity::PUZZLE_HOLE_2 :
case TR::Entity::PUZZLE_HOLE_3 :
case TR::Entity::PUZZLE_HOLE_4 :
case TR::Entity::KEY_HOLE_1 :
case TR::Entity::KEY_HOLE_2 :
case TR::Entity::KEY_HOLE_3 :
case TR::Entity::KEY_HOLE_4 : return new KeyHole(this, index);
case TR::Entity::SCION_TARGET : return new ScionTarget(this, index);
case TR::Entity::WATERFALL : return new Waterfall(this, index);
case TR::Entity::TRAP_LAVA : return new TrapLava(this, index);
case TR::Entity::CABIN : return new Cabin(this, index);
case TR::Entity::TRAP_FLAME_EMITTER : return new TrapFlameEmitter(this, index);
case TR::Entity::BOAT : return new Boat(this, index);
case TR::Entity::EARTHQUAKE : return new Earthquake(this, index);
case TR::Entity::MUTANT_EGG_SMALL :
case TR::Entity::MUTANT_EGG_BIG : return new MutantEgg(this, index);
default : return (level.entities[index].modelIndex > 0) ? new Controller(this, index) : new Sprite(this, index, 0);
}
}
static void fillCallback(int id, int width, int height, int tileX, int tileY, void *userData, void *data) { static void fillCallback(int id, int width, int height, int tileX, int tileY, void *userData, void *data) {
static const uint32 barColor[UI::BAR_MAX][25] = { static const uint32 barColor[UI::BAR_MAX][25] = {
// health bar // health bar

View File

@@ -61,10 +61,9 @@ struct Gear : Controller {
struct Dart : Controller { struct Dart : Controller {
vec3 velocity; vec3 velocity;
vec3 dir; vec3 dir;
bool inWall; // dart starts from wall
bool armed; bool armed;
Dart(IGame *game, int entity) : Controller(game, entity), inWall(true), armed(true) { Dart(IGame *game, int entity) : Controller(game, entity), armed(true) {
dir = vec3(sinf(angle.y), 0, cosf(angle.y)); dir = vec3(sinf(angle.y), 0, cosf(angle.y));
} }
@@ -83,7 +82,6 @@ struct Dart : Controller {
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 (pos.y > info.floor || pos.y < info.ceiling || !insideRoom(pos, getRoomIndex())) { if (pos.y > info.floor || pos.y < info.ceiling || !insideRoom(pos, getRoomIndex())) {
if (!inWall) {
TR::Entity &e = getEntity(); TR::Entity &e = getEntity();
vec3 p = pos - dir * 64.0f; // wall offset = 64 vec3 p = pos - dir * 64.0f; // wall offset = 64
@@ -92,8 +90,6 @@ struct Dart : Controller {
level->entityRemove(entity); level->entityRemove(entity);
delete this; delete this;
} }
} else
inWall = false;
} }
}; };
@@ -105,18 +101,18 @@ struct TrapDartEmitter : Controller {
TrapDartEmitter(IGame *game, int entity) : Controller(game, entity) {} TrapDartEmitter(IGame *game, int entity) : Controller(game, entity) {}
virtual bool activate() { void virtual update() {
if (!Controller::activate()) if (state == STATE_IDLE || state == STATE_FIRE)
return false; animation.setState(isActive() ? STATE_FIRE : STATE_IDLE);
else
ASSERT(false);
animation.setState(STATE_FIRE); if (state == STATE_FIRE && animation.framePrev == -1) {
// add dart (bullet)
TR::Entity &entity = getEntity(); TR::Entity &entity = getEntity();
vec3 p = pos + vec3(0.0f, -512.0f, 256.0f).rotateY(PI - entity.rotation); vec3 p = pos + vec3(0.0f, -512.0f, 256.0f).rotateY(PI - entity.rotation);
int index = level->entityAdd(TR::Entity::TRAP_DART, getRoomIndex(), int(pos.x), int(pos.y), int(pos.z), entity.rotation, entity.intensity); int index = level->entityAdd(TR::Entity::TRAP_DART, getRoomIndex(), int(p.x), int(p.y), int(p.z), entity.rotation, -1);
if (index > -1) { if (index > -1) {
Dart *dart = new Dart(game, index); Dart *dart = new Dart(game, index);
dart->activate(); dart->activate();
@@ -125,17 +121,10 @@ struct TrapDartEmitter : Controller {
Sprite::add(game, TR::Entity::SMOKE, entity.room, (int)p.x, (int)p.y, (int)p.z); Sprite::add(game, TR::Entity::SMOKE, entity.room, (int)p.x, (int)p.y, (int)p.z);
game->playSound(TR::SND_DART, pos, Sound::Flags::PAN); game->playSound(TR::SND_DART, p, Sound::Flags::PAN);
return true;
} }
void virtual update() {
updateAnimation(true); updateAnimation(true);
if (animation.canSetState(STATE_IDLE)) {
animation.setState(STATE_IDLE);
deactivate();
}
} }
}; };
@@ -909,6 +898,7 @@ struct MutantEgg : Controller {
} }
}; };
struct KeyHole : Controller { struct KeyHole : Controller {
KeyHole(IGame *game, int entity) : Controller(game, entity) {} KeyHole(IGame *game, int entity) : Controller(game, entity) {}
@@ -927,6 +917,33 @@ struct KeyHole : Controller {
}; };
struct Earthquake : Controller {
float timer;
Earthquake(IGame *game, int entity) : Controller(game, entity), timer(0.0f) {}
virtual void update() {
if (!isActive()) return;
if (timer < (1.0f / 30.0f)){
timer += Core::deltaTime;
return;
}
float p = randf();
if (p < 0.001f) {
game->playSound(TR::SND_STOMP);
game->getCamera()->shake = 1.0f;
} else if (p < 0.04f) {
game->playSound(TR::SND_BOULDER);
game->getCamera()->shake = 0.3f;
}
timer = 0.0f;
}
};
struct Waterfall : Controller { struct Waterfall : Controller {
#define SPLASH_TIMESTEP (1.0f / 30.0f) #define SPLASH_TIMESTEP (1.0f / 30.0f)