mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-16 18:14:05 +02:00
#14 Natla AI
This commit is contained in:
@@ -92,8 +92,8 @@ struct IGame {
|
||||
virtual bool invChooseKey(int playerIndex, TR::Entity::Type hole) { return false; }
|
||||
|
||||
virtual Sound::Sample* playSound(int id, const vec3 &pos = vec3(0.0f), int flags = 0) const { return NULL; }
|
||||
virtual void playTrack(uint8 track) {}
|
||||
virtual void stopTrack() {}
|
||||
virtual void playTrack(uint8 track, bool background = false) {}
|
||||
virtual void stopTrack() {}
|
||||
};
|
||||
|
||||
struct Controller {
|
||||
|
202
src/enemy.h
202
src/enemy.h
@@ -348,6 +348,7 @@ struct Enemy : Character {
|
||||
return false;
|
||||
thinkTime -= 1.0f / 30.0f;
|
||||
|
||||
int zoneOld = zone;
|
||||
updateZone();
|
||||
|
||||
target = (Character*)game->getLara(pos);
|
||||
@@ -422,6 +423,9 @@ struct Enemy : Character {
|
||||
if (path && this->box != path->boxes[path->index - 1] && this->box != path->boxes[path->index])
|
||||
targetBoxOld = -1;
|
||||
|
||||
if (zoneOld != zone)
|
||||
targetBoxOld = -1;
|
||||
|
||||
if (targetBoxOld != targetBox) {
|
||||
if (findPath(stepHeight, dropHeight, getEntity().isBigEnemy()))
|
||||
nextWaypoint();
|
||||
@@ -512,7 +516,7 @@ struct Enemy : Character {
|
||||
return false;
|
||||
}
|
||||
|
||||
void shot(TR::Entity::Type type, int joint, const vec3 &offset) {
|
||||
void shot(TR::Entity::Type type, int joint, const vec3 &offset, float rx, float ry) {
|
||||
vec3 from = getJoint(joint) * offset;
|
||||
vec3 to = target->getBoundingBox().center();
|
||||
|
||||
@@ -520,11 +524,15 @@ struct Enemy : Character {
|
||||
if (bullet) {
|
||||
vec3 dir = to - from;
|
||||
vec3 ang = vec3(-atan2f(dir.y, sqrtf(dir.x * dir.x + dir.z * dir.z)), atan2f(dir.x, dir.z), 0.0f);
|
||||
ang += vec3(randf() * 2.0f - 1.0f, randf() * 2.0f - 1.0f, 0.0f) * (1.5f * DEG2RAD);
|
||||
ang += vec3(rx, ry, 0.0f);
|
||||
bullet->setAngle(ang);
|
||||
}
|
||||
}
|
||||
|
||||
void shot(TR::Entity::Type type, int joint, const vec3 &offset) {
|
||||
shot(type, joint, offset, (randf() * 2.0f - 1.0f) * (1.5f * DEG2RAD), (randf() * 2.0f - 1.0f) * (1.5f * DEG2RAD));
|
||||
}
|
||||
|
||||
bool isVisible() {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
ICamera *camera = game->getCamera(i);
|
||||
@@ -2777,7 +2785,7 @@ struct SkaterBoy : Human {
|
||||
fullChestRotation = state == STATE_STAND_FIRE || state == STATE_MOVE_FIRE;
|
||||
|
||||
if (health < 120)
|
||||
game->playTrack(56);
|
||||
game->playTrack(56, true);
|
||||
|
||||
switch (state) {
|
||||
case STATE_STOP :
|
||||
@@ -2982,10 +2990,196 @@ struct MrT : Human {
|
||||
}
|
||||
};
|
||||
|
||||
#define NATLA_FIRE_ANGLE (30 * DEG2RAD)
|
||||
#define NATLA_FAINT_TIME 16.0f
|
||||
#define NATLA_HALF_HEALTH 200.0f
|
||||
#define NATLA_FAINT_HEALTH -8192.0f
|
||||
#define NATLA_LIFT_SPEED MUTANT_LIFT_SPEED
|
||||
|
||||
struct Natla : Human {
|
||||
|
||||
Natla(IGame *game, int entity) : Human(game, entity, 400) {}
|
||||
enum {
|
||||
STATE_NONE,
|
||||
STATE_STOP,
|
||||
STATE_FLY,
|
||||
STATE_RUN,
|
||||
STATE_AIM,
|
||||
STATE_FAINT,
|
||||
STATE_FIRE,
|
||||
STATE_FALL,
|
||||
STATE_STAND,
|
||||
STATE_DEATH,
|
||||
};
|
||||
|
||||
enum {
|
||||
FLAG_FLY = 1,
|
||||
};
|
||||
|
||||
Natla(IGame *game, int entity) : Human(game, entity, 400) {
|
||||
jointChest = 1;
|
||||
jointHead = 2;
|
||||
}
|
||||
|
||||
int stage1() {
|
||||
flying = (flags.unused & FLAG_FLY);
|
||||
|
||||
stepHeight = 256;
|
||||
dropHeight = -256;
|
||||
|
||||
if (flying) {
|
||||
stepHeight *= 80;
|
||||
dropHeight *= 80;
|
||||
}
|
||||
|
||||
if (!think(false))
|
||||
return state;
|
||||
|
||||
timer += Core::deltaTime;
|
||||
bool canShot = target && target->health > 0.0f && targetIsVisible(HUMAN_DIST_SHOT);
|
||||
|
||||
if (canShot && state == STATE_FLY && flying && rand() < 256)
|
||||
flags.unused &= ~FLAG_FLY;
|
||||
else if (!canShot)
|
||||
flags.unused |= FLAG_FLY;
|
||||
|
||||
flying = (flags.unused & FLAG_FLY);
|
||||
|
||||
if (state == nextState)
|
||||
nextState = STATE_NONE;
|
||||
|
||||
switch (state) {
|
||||
case STATE_STOP :
|
||||
timer = 0.0f;
|
||||
return flying ? STATE_FLY : STATE_AIM;
|
||||
case STATE_FLY : {
|
||||
if (timer >= 1.0f) {
|
||||
{ //if (canShot) { ???
|
||||
game->playSound(TR::SND_NATLA_SHOT, pos, Sound::PAN);
|
||||
shot(TR::Entity::CENTAUR_BULLET, 4, vec3(5.0f, 220.0f, 7.0f));
|
||||
}
|
||||
timer -= 1.0f;
|
||||
}
|
||||
|
||||
int16 roomIndex = getRoomIndex();
|
||||
TR::Room::Sector *sector = level->getSector(roomIndex, pos);
|
||||
float floor = level->getFloor(sector, pos) - 128.0f;
|
||||
|
||||
if (!flying && pos.y >= floor)
|
||||
return STATE_STOP;
|
||||
|
||||
break;
|
||||
}
|
||||
case STATE_AIM :
|
||||
if (nextState != STATE_NONE)
|
||||
return nextState;
|
||||
return canShot ? STATE_FIRE : STATE_STOP;
|
||||
case STATE_FIRE :
|
||||
if (nextState != STATE_NONE)
|
||||
return state;
|
||||
|
||||
game->playSound(TR::SND_NATLA_SHOT, pos, Sound::PAN);
|
||||
shot(TR::Entity::CENTAUR_BULLET, 4, vec3(5.0f, 220.0f, 7.0f));
|
||||
shot(TR::Entity::CENTAUR_BULLET, 4, vec3(5.0f, 220.0f, 7.0f), 0.0f, (randf() * 2.0f - 1.0f) * (25.0f * DEG2RAD));
|
||||
shot(TR::Entity::CENTAUR_BULLET, 4, vec3(5.0f, 220.0f, 7.0f), 0.0f, (randf() * 2.0f - 1.0f) * (25.0f * DEG2RAD));
|
||||
|
||||
nextState = STATE_STOP;
|
||||
break;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
int stage2() {
|
||||
stepHeight = 256;
|
||||
dropHeight = -256;
|
||||
flying = false;
|
||||
|
||||
if (!think(true))
|
||||
return state;
|
||||
|
||||
timer += Core::deltaTime;
|
||||
bool canShot = target && target->health > 0.0f && fabsf(targetAngle) < NATLA_FIRE_ANGLE && targetIsVisible(HUMAN_DIST_SHOT);
|
||||
|
||||
switch (state) {
|
||||
case STATE_RUN :
|
||||
case STATE_STAND :
|
||||
if (timer >= 20.0f / 30.0f) {
|
||||
{ // if (canShot) { ???
|
||||
game->playSound(TR::SND_NATLA_SHOT, pos, Sound::PAN);
|
||||
shot(TR::Entity::MUTANT_BULLET, 4, vec3(5.0f, 220.0f, 7.0f));
|
||||
}
|
||||
timer -= 20.0f / 30.0f;
|
||||
}
|
||||
return canShot ? STATE_STAND : STATE_RUN;
|
||||
case STATE_FLY :
|
||||
health = NATLA_FAINT_HEALTH;
|
||||
return STATE_FALL;
|
||||
case STATE_STOP :
|
||||
case STATE_AIM :
|
||||
case STATE_FIRE :
|
||||
health = NATLA_FAINT_HEALTH;
|
||||
return STATE_FAINT;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
virtual int getStateGround() {
|
||||
if (health > NATLA_HALF_HEALTH)
|
||||
return stage1();
|
||||
return stage2();
|
||||
}
|
||||
|
||||
virtual int getStateDeath() {
|
||||
switch (state) {
|
||||
case STATE_FALL : {
|
||||
int16 roomIndex = getRoomIndex();
|
||||
TR::Room::Sector *sector = level->getSector(roomIndex, pos);
|
||||
float floor = level->getFloor(sector, pos);
|
||||
if (pos.y >= floor) {
|
||||
pos.y = floor;
|
||||
timer = 0.0f;
|
||||
return STATE_FAINT;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
case STATE_FAINT : {
|
||||
timer += Core::deltaTime;
|
||||
if (timer >= NATLA_FAINT_TIME) {
|
||||
health = NATLA_HALF_HEALTH;
|
||||
timer = 0.0f;
|
||||
flags.unused = 0;
|
||||
game->playTrack(54, true);
|
||||
return STATE_STAND;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
}
|
||||
return STATE_DEATH;
|
||||
}
|
||||
|
||||
virtual void updateVelocity() {
|
||||
if (state == STATE_FLY) {
|
||||
angle.y += targetAngle;
|
||||
updateJoints();
|
||||
angle.y -= targetAngle;
|
||||
}
|
||||
Enemy::updateVelocity();
|
||||
}
|
||||
|
||||
virtual void updatePosition() {
|
||||
if (flying) {
|
||||
stand = STAND_AIR;
|
||||
lift(target->pos.y - pos.y, NATLA_LIFT_SPEED);
|
||||
}
|
||||
|
||||
turn(true, state == STATE_RUN ? HUMAN_TURN_FAST : HUMAN_TURN_SLOW);
|
||||
|
||||
Enemy::updatePosition();
|
||||
setOverrides(state != STATE_FAINT, jointChest, jointHead);
|
||||
lookAt(target);
|
||||
|
||||
stand = STAND_GROUND;
|
||||
}
|
||||
};
|
||||
|
||||
struct Dog : Enemy {
|
||||
|
@@ -960,6 +960,8 @@ namespace TR {
|
||||
|
||||
SND_STAIRS2SLOPE = 119,
|
||||
|
||||
SND_NATLA_SHOT = 123,
|
||||
|
||||
SND_HIT_SKATEBOY = 132,
|
||||
|
||||
SND_HIT_MUTANT = 142,
|
||||
@@ -2250,7 +2252,7 @@ namespace TR {
|
||||
ByteFlags flipmaps[MAX_FLIPMAP_COUNT];
|
||||
ByteFlags tracks[MAX_TRACKS_COUNT];
|
||||
|
||||
uint16 fogColor;
|
||||
uint16 fogColor;
|
||||
union {
|
||||
struct { uint16 track:8, flipped:1; };
|
||||
uint16 value;
|
||||
|
14
src/lara.h
14
src/lara.h
@@ -527,14 +527,16 @@ struct Lara : Character {
|
||||
//reset(57, vec3(71081, 5632, 73042), 0); // Level 10a (Skaterboy)
|
||||
//reset(68, vec3(52458, -9984, 93724), 270 * DEG2RAD); // Level 10a (MrT)
|
||||
//reset(44, vec3(75803, -11008, 21097), 90 * DEG2RAD); // Level 10a (boat)
|
||||
//reset(47, vec3(50546, -13056, 53783), 270 * DEG2RAD); // Level 10b (trap door slope)
|
||||
//reset(59, vec3(42907, -13056, 63012), 270 * DEG2RAD); // Level 10b (doppelganger)
|
||||
//reset(53, vec3(39617, -18385, 48950), 180 * DEG2RAD); // Level 10b (centaur)
|
||||
//reset(50, vec3(52122, -18688, 47313), 150 * DEG2RAD); // Level 10b (scion holder pickup)
|
||||
//reset(47, vec3(50546, -13056, 53783), 270 * DEG2RAD); // Level 10b (trap door slope)
|
||||
//reset(59, vec3(42907, -13056, 63012), 270 * DEG2RAD); // Level 10b (doppelganger)
|
||||
//reset(53, vec3(39617, -18385, 48950), 180 * DEG2RAD); // Level 10b (centaur)
|
||||
//reset(50, vec3(52122, -18688, 47313), 150 * DEG2RAD); // Level 10b (scion holder pickup)
|
||||
//reset(50, vec3(53703, -18688, 13769), PI); // Level 10c (scion holder)
|
||||
//reset(19, vec3(35364, -512, 40199), PI * 0.5f); // Level 10c (lava flow)
|
||||
//reset(9, vec3(69074, -14592, 25192), 0); // Level 10c (trap slam)
|
||||
//reset(21, vec3(47668, -10752, 32163), 0); // Level 10c (lava emitter)
|
||||
//reset(29, vec3(61586, -439, 51734), PI * 0.5f); // Level 10c (precise falling)
|
||||
//reset(33, vec3(64641, 9578, 61861), 0); // Level 10c (Natla)
|
||||
//reset(10, vec3(90443, 11264 - 256, 114614), PI, STAND_ONWATER); // villa mortal 2
|
||||
//dbgBoxes = NULL;
|
||||
|
||||
@@ -1720,7 +1722,7 @@ struct Lara : Character {
|
||||
}
|
||||
|
||||
int doTutorial(int track) {
|
||||
if (level->version == TR::VER_TR1_PC || level->version == TR::VER_TR1_PSX)
|
||||
if (level->version & TR::VER_TR1)
|
||||
switch (track) { // GYM tutorial routine
|
||||
case 28 : if (level->state.tracks[track].once && state == STATE_UP_JUMP) track = 29; break;
|
||||
case 37 :
|
||||
@@ -2036,7 +2038,7 @@ struct Lara : Character {
|
||||
if (!(level->state.progress.secrets & (1 << cmd.args))) {
|
||||
level->state.progress.secrets |= 1 << cmd.args;
|
||||
if (!game->playSound(TR::SND_SECRET, pos))
|
||||
game->playTrack(TR::TRACK_TR1_SECRET);
|
||||
game->playTrack(TR::TRACK_TR1_SECRET, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
16
src/level.h
16
src/level.h
@@ -607,7 +607,7 @@ struct Level : IGame {
|
||||
void stopChannel(Sound::Sample *channel) {
|
||||
if (channel == sndTrack) {
|
||||
sndTrack = NULL;
|
||||
if (level.state.flags.track == TR::LEVEL_INFO[level.id].track) // play ambient track
|
||||
if (level.state.flags.track != TR::LEVEL_INFO[level.id].track) // play ambient track
|
||||
playTrack(0);
|
||||
}
|
||||
}
|
||||
@@ -636,7 +636,19 @@ struct Level : IGame {
|
||||
delete req;
|
||||
}
|
||||
|
||||
virtual void playTrack(uint8 track) {
|
||||
static void playAsyncBG(Stream *stream, void *userData) {
|
||||
TrackRequest *req = (TrackRequest*)userData;
|
||||
if (stream)
|
||||
Sound::play(stream, NULL, 1.0f, 1.0f, req->flags);
|
||||
delete req;
|
||||
}
|
||||
|
||||
virtual void playTrack(uint8 track, bool background = false) {
|
||||
if (background) {
|
||||
TR::getGameTrack(level.version, track, playAsyncBG, new TrackRequest(this, Sound::MUSIC));
|
||||
return;
|
||||
}
|
||||
|
||||
if (track == 0)
|
||||
track = TR::LEVEL_INFO[level.id].track;
|
||||
|
||||
|
@@ -857,7 +857,7 @@ struct Video {
|
||||
height = (frame.height + 15) / 16 * 16;
|
||||
fps = 150 / (frame.size / VIDEO_CHUNK_SIZE);
|
||||
fps = (fps < 20) ? 15 : 30;
|
||||
//fps = int(fps * 44100 / 37800);
|
||||
|
||||
stream->setPos(pos);
|
||||
|
||||
vFrameIndex = aFrameIndex = -1;
|
||||
|
Reference in New Issue
Block a user