From c208ece45ccc99e322938ebd1ede0846a55ba74a Mon Sep 17 00:00:00 2001 From: XProger Date: Mon, 2 Oct 2017 07:09:44 +0300 Subject: [PATCH] #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 --- src/controller.h | 33 +++++- src/enemy.h | 76 +++++++++++++- src/format.h | 12 +-- src/lara.h | 15 ++- src/level.h | 265 ++++++++++++++++------------------------------- src/trigger.h | 97 ++++++++++------- 6 files changed, 265 insertions(+), 233 deletions(-) diff --git a/src/controller.h b/src/controller.h index 2bc0f2d..c86167e 100644 --- a/src/controller.h +++ b/src/controller.h @@ -363,7 +363,7 @@ struct Controller { void getSpheres(Sphere *spheres, int &count) { TR::Model *m = getModel(); Basis basis(getMatrix()); - + // TODO: optimize (check frame index for animation updates, use joints array) count = 0; for (int i = 0; i < m->mCount; 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)); 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); game->addSprite(TR::Entity::EXPLOSION, part.roomIndex, int(p.x), int(p.y), int(p.z)); diff --git a/src/enemy.h b/src/enemy.h index f3a4865..6721cce 100644 --- a/src/enemy.h +++ b/src/enemy.h @@ -68,7 +68,7 @@ struct Enemy : Character { bool targetFromView; // enemy in target view zone 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; targetInView = targetFromView = targetCanAttack = false; } @@ -78,7 +78,7 @@ struct Enemy : Character { } virtual bool activate() { - if (Character::activate()) { + if (health > 0.0f && Character::activate()) { target = (Character*)game->getLara(); 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 \ No newline at end of file diff --git a/src/format.h b/src/format.h index 4e20ba1..bdfc253 100644 --- a/src/format.h +++ b/src/format.h @@ -17,7 +17,7 @@ E( LARA_MAGNUMS ) \ E( LARA_UZIS ) \ E( LARA_SPEC ) \ - E( ENEMY_TWIN ) \ + E( ENEMY_DOPPELGANGER ) \ E( ENEMY_WOLF ) \ E( ENEMY_BEAR ) \ E( ENEMY_BAT ) \ @@ -154,10 +154,10 @@ E( KEY_HOLE_4 ) \ E( UNUSED_4 ) \ E( UNUSED_5 ) \ - E( SCION_1 ) \ - E( SCION_2 ) \ - E( SCION_3 ) \ + E( SCION_QUALOPEC ) \ + E( SCION_DROP ) \ E( SCION_TARGET ) \ + E( SCION_NATLA ) \ E( SCION_HOLDER ) \ E( UNUSED_6 ) \ E( UNUSED_7 ) \ @@ -647,7 +647,7 @@ namespace TR { void *controller; // Controller implementation or NULL 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 { @@ -669,7 +669,7 @@ namespace TR { return (type >= PISTOLS && type <= AMMO_UZIS) || (type >= PUZZLE_1 && type <= PUZZLE_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 { diff --git a/src/lara.h b/src/lara.h index 52ea9d1..a1b30f3 100644 --- a/src/lara.h +++ b/src/lara.h @@ -426,17 +426,14 @@ struct Lara : Character { if (level->extra.braid > -1) 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 //reset(14, vec3(40448, 3584, 60928), PI * 0.5f, STAND_ONWATER); // gym (pool) //reset(0, vec3(74858, 3072, 20795), 0); // level 1 (dart) //reset(14, vec3(20215, 6656, 52942), PI); // level 1 (bridge) //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(9, vec3(63008, 0, 37787), 0); // level 2 (switch) //reset(15, vec3(70067, -256, 29104), -0.68f); // level 2 (pool) //reset(26, vec3(71980, 1546, 19000), 270 * DEG2RAD); // level 2 (underwater switch) //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(19, vec3(33368, 19968, 45643), 270 * DEG2RAD); // level 4 (damocles) //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(73, vec3(73372, -122, 51687), PI * 0.5f); // level 6 (midas hand) //reset(64, vec3(36839, -2560, 48769), 270 * DEG2RAD); // level 6 (flipmap effect) @@ -842,7 +840,8 @@ struct Lara : Character { if (arm->target && checkHit(arm->target, p, hit, hit)) { ((Character*)arm->target)->hit(wpnGetDamage()); hit -= d * 64.0f; - Sprite::add(game, TR::Entity::BLOOD, room, (int)hit.x, (int)hit.y, (int)hit.z, Sprite::FRAME_ANIMATED); + 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); } else { hit -= d * 64.0f; Sprite::add(game, TR::Entity::RICOCHET, room, (int)hit.x, (int)hit.y, (int)hit.z, Sprite::FRAME_RANDOM); @@ -1494,7 +1493,7 @@ struct Lara : Character { controller->angle.x = -25 * DEG2RAD; controller->angle.y = angle.y; - if (item.type == TR::Entity::SCION_1) + if (item.type == TR::Entity::SCION_QUALOPEC) limit = TR::Limits::SCION; if (!checkInteraction(controller, limit, (input & ACTION) != 0)) @@ -1505,7 +1504,7 @@ struct Lara : Character { 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); ((Camera*)level->cameraController)->state = Camera::STATE_CUTSCENE; level->cutMatrix.identity(); diff --git a/src/level.h b/src/level.h index 320c6bc..9065ade 100644 --- a/src/level.h +++ b/src/level.h @@ -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); 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); + Controller *enemy = initController(index); e.controller = enemy; e.flags.active = TR::ACTIVE; enemy->activate(); @@ -357,163 +341,10 @@ struct Level : IGame { initOverrides(); for (int i = 0; i < level.entitiesBaseCount; i++) { - TR::Entity &entity = level.entities[i]; - switch (entity.type) { - case TR::Entity::LARA : - case TR::Entity::CUT_1 : - 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); - } + TR::Entity &e = level.entities[i]; + e.controller = initController(i); + if (e.type == TR::Entity::LARA || e.type == TR::Entity::CUT_1) + lara = (Lara*)e.controller; } if (level.id != TR::TITLE) { @@ -583,6 +414,92 @@ struct Level : IGame { 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 const uint32 barColor[UI::BAR_MAX][25] = { // health bar diff --git a/src/trigger.h b/src/trigger.h index df780fc..65efdf1 100644 --- a/src/trigger.h +++ b/src/trigger.h @@ -61,10 +61,9 @@ struct Gear : Controller { struct Dart : Controller { vec3 velocity; vec3 dir; - bool inWall; // dart starts from wall 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)); } @@ -83,17 +82,14 @@ struct Dart : Controller { TR::Level::FloorInfo 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 (!inWall) { - TR::Entity &e = getEntity(); + TR::Entity &e = getEntity(); - vec3 p = pos - dir * 64.0f; // wall offset = 64 - Sprite::add(game, TR::Entity::RICOCHET, e.room, (int)p.x, (int)p.y, (int)p.z, Sprite::FRAME_RANDOM); + vec3 p = pos - dir * 64.0f; // wall offset = 64 + Sprite::add(game, TR::Entity::RICOCHET, e.room, (int)p.x, (int)p.y, (int)p.z, Sprite::FRAME_RANDOM); - level->entityRemove(entity); - delete this; - } - } else - inWall = false; + level->entityRemove(entity); + delete this; + } } }; @@ -105,37 +101,30 @@ struct TrapDartEmitter : Controller { TrapDartEmitter(IGame *game, int entity) : Controller(game, entity) {} - virtual bool activate() { - if (!Controller::activate()) - return false; - - animation.setState(STATE_FIRE); - - // add dart (bullet) - TR::Entity &entity = getEntity(); - - 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); - if (index > -1) { - Dart *dart = new Dart(game, index); - dart->activate(); - level->entities[index].controller = dart; - } - - 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); - - return true; - } - void virtual update() { - updateAnimation(true); - if (animation.canSetState(STATE_IDLE)) { - animation.setState(STATE_IDLE); - deactivate(); + if (state == STATE_IDLE || state == STATE_FIRE) + animation.setState(isActive() ? STATE_FIRE : STATE_IDLE); + else + ASSERT(false); + + if (state == STATE_FIRE && animation.framePrev == -1) { + TR::Entity &entity = getEntity(); + + vec3 p = pos + vec3(0.0f, -512.0f, 256.0f).rotateY(PI - entity.rotation); + + int index = level->entityAdd(TR::Entity::TRAP_DART, getRoomIndex(), int(p.x), int(p.y), int(p.z), entity.rotation, -1); + if (index > -1) { + Dart *dart = new Dart(game, index); + dart->activate(); + level->entities[index].controller = dart; + } + + Sprite::add(game, TR::Entity::SMOKE, entity.room, (int)p.x, (int)p.y, (int)p.z); + + game->playSound(TR::SND_DART, p, Sound::Flags::PAN); } + + updateAnimation(true); } }; @@ -909,6 +898,7 @@ struct MutantEgg : Controller { } }; + struct KeyHole : Controller { 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 { #define SPLASH_TIMESTEP (1.0f / 30.0f)