1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-01 02:40:43 +02:00

#368 GBA T-Rex AI

This commit is contained in:
XProger
2022-05-07 04:49:04 +03:00
parent 7da3aadff0
commit d91a919f56
6 changed files with 265 additions and 80 deletions

View File

@@ -449,11 +449,14 @@ void Camera::update()
if (center)
{
int32 offset = (box.minZ + box.maxZ) >> 1;
int32 dx = (box.minX + box.maxX) >> 1;
int32 dz = (box.minZ + box.maxZ) >> 1;
int32 s, c;
sincos(item->angle.y, s, c);
target.pos.x += (s * offset) >> FIXED_SHIFT;
target.pos.z += (c * offset) >> FIXED_SHIFT;
X_ROTXY(dz, dx, s, c);
target.pos.x += dx;
target.pos.z += dz;
}
lastFixed = (int32(lastFixed) ^ int32(isFixed)) != 0; // armcpp 3DO compiler (lastFixed ^= isFixed)

View File

@@ -154,7 +154,20 @@
#endif
#ifdef _DEBUG
#define LOG(...) printf(__VA_ARGS__)
#if defined(__WIN32__)
#include <stdio.h>
inline void LOG(const char* format, ...)
{
char str[1024];
va_list arglist;
va_start(arglist, format);
_vsnprintf(str, 1024, format, arglist);
va_end(arglist);
OutputDebugStringA(str);
}
#else
#define LOG(...) printf(__VA_ARGS__)
#endif
#else
#define LOG(...)
#endif
@@ -1776,6 +1789,8 @@ struct ItemObj
uint32 updateHitMask(Lara* lara, CollisionInfo* cinfo);
void meshSwap(ItemType type, uint32 mask);
ItemObj* init(Room* room);
X_INLINE ItemObj() {}
@@ -2203,7 +2218,8 @@ struct TargetInfo
uint16 boxIndexTarget;
uint16 zoneIndex;
uint16 zoneIndexTarget;
bool aim;
bool front;
bool behind;
bool canAttack;
};

View File

@@ -134,7 +134,8 @@ struct Enemy : ItemObj
tinfo.dist = 16 * 1024;
tinfo.angle = 0;
tinfo.aim = false;
tinfo.front = false;
tinfo.behind = false;
tinfo.canAttack = false;
tinfo.rotHead = 0;
@@ -172,9 +173,10 @@ struct Enemy : ItemObj
tinfo.dist = fastLength(dx, dz);
tinfo.angle = rot - angle.y;
tinfo.aim = (tinfo.angle > -ANGLE_90) && (tinfo.angle < ANGLE_90);
tinfo.canAttack = tinfo.aim && (abs(tinfo.target->pos.y - pos.y) < 256);
tinfo.rotHead = (tinfo.aim && mood != MOOD_SLEEP) ? tinfo.angle : 0;
tinfo.front = abs(tinfo.angle) < ANGLE_90;
tinfo.behind = abs(rot - tinfo.target->angle.y - ANGLE_180) < ANGLE_90;
tinfo.canAttack = tinfo.front && (abs(tinfo.target->pos.y - pos.y) < 256);
tinfo.rotHead = (tinfo.front && mood != MOOD_SLEEP) ? tinfo.angle : 0;
}
void updateMood()
@@ -438,7 +440,7 @@ struct Enemy : ItemObj
return level.zones[gSaveGame.flipped][extraE->nav.zoneType];
}
void bite(int32 joint, const vec3i &offset, int32 damage)
void bite(int32 damage, const vec3i &offset, int32 joint)
{
if (!tinfo.target)
return;
@@ -731,16 +733,18 @@ struct Doppelganger : Enemy
struct Wolf : Enemy
{
enum {
HIT_MASK = 0x774F, // body, head, front legs
DIST_STALK = 1023 * 3,
DIST_BITE = 345,
DIST_ATTACK = 1024 + 512
};
HIT_MASK = 0x774F, // body, head, front legs
DIST_STALK = 1023 * 3,
DIST_BITE = 345,
DIST_ATTACK = 1024 + 512,
DAMAGE_BITE = 100,
DAMAGE_ATTACK = 50,
enum {
ANIM_DEATH = 20,
ANIM_DEATH_RUN,
ANIM_DEATH_JUMP
ANIM_DEATH_RUN = 21,
ANIM_DEATH_JUMP = 22
};
enum {
@@ -780,10 +784,15 @@ struct Wolf : Enemy
switch (state)
{
case STATE_STOP: {
return (nextState != STATE_NONE) ? nextState : STATE_WALK;
case STATE_STOP:
{
if (nextState)
nextState;
return STATE_WALK;
}
case STATE_WALK: {
case STATE_WALK:
{
extraE->maxTurn = ENEMY_TURN_2;
tinfo.tilt = tinfo.turn >> 1;
@@ -797,13 +806,15 @@ struct Wolf : Enemy
}
break;
}
case STATE_RUN: {
case STATE_RUN:
{
extraE->maxTurn = ENEMY_TURN_5;
tinfo.tilt = tinfo.turn;
if (tinfo.aim && tinfo.dist < DIST_ATTACK)
if (tinfo.front && tinfo.dist < DIST_ATTACK)
{
if (tinfo.dist <= (DIST_ATTACK >> 1))
if (tinfo.dist <= (DIST_ATTACK >> 1) && !tinfo.behind)
{
nextState = STATE_NONE;
return STATE_ATTACK;
@@ -820,17 +831,23 @@ struct Wolf : Enemy
return STATE_GROWL;
break;
}
case STATE_STALK: {
case STATE_STALK:
{
extraE->maxTurn = ENEMY_TURN_2;
if (mood == MOOD_ESCAPE)
return STATE_RUN;
if (tinfo.dist < DIST_BITE && tinfo.canAttack && (tinfo.target->health > 0))
if ((tinfo.dist < DIST_BITE) && tinfo.canAttack && (tinfo.target->health > 0))
return STATE_BITE;
if (tinfo.dist > DIST_STALK)
return STATE_RUN;
if (mood == MOOD_ATTACK && (!tinfo.aim || tinfo.dist > DIST_ATTACK))
return STATE_RUN;
if (mood == MOOD_ATTACK)
{
if (!tinfo.front || (tinfo.dist > DIST_ATTACK) || tinfo.behind)
return STATE_RUN;
break;
}
if (rand_logic() < 0x180)
{
nextState = STATE_HOWL;
@@ -840,7 +857,9 @@ struct Wolf : Enemy
return STATE_GROWL;
break;
}
case STATE_SLEEP: {
case STATE_SLEEP:
{
if ((mood == MOOD_ESCAPE) || checkZone())
nextState = STATE_GROWL;
else if (rand_logic() < 0x20)
@@ -849,8 +868,10 @@ struct Wolf : Enemy
break;
return STATE_STOP;
}
case STATE_GROWL: {
if (nextState != STATE_NONE)
case STATE_GROWL:
{
if (nextState)
return nextState;
if (mood == MOOD_ESCAPE)
return STATE_RUN;
@@ -862,18 +883,22 @@ struct Wolf : Enemy
return STATE_STOP;
return STATE_RUN;
}
case STATE_ATTACK: {
case STATE_ATTACK:
{
tinfo.tilt = tinfo.turn;
if ((nextState == STATE_NONE) && (hitMask & HIT_MASK)) {
bite(6, _vec3i(0, -14, 174), 50);
if (!nextState && (hitMask & HIT_MASK)) {
bite(DAMAGE_ATTACK, _vec3i(0, -14, 174), 6);
nextState = STATE_RUN;
}
return STATE_RUN;
}
case STATE_BITE: {
if ((nextState == STATE_NONE) && (hitMask & HIT_MASK)) {
bite(6, _vec3i(0, -14, 174), 100);
case STATE_BITE:
{
if (!nextState && (hitMask & HIT_MASK)) {
bite(DAMAGE_BITE, _vec3i(0, -14, 174), 6);
nextState = STATE_GROWL;
}
return STATE_RUN;
@@ -894,7 +919,11 @@ struct Wolf : Enemy
struct Bear : Enemy
{
enum {
HIT_MASK = 0x2406C // front legs and head
HIT_MASK = 0x2406C, // front legs and head
DAMAGE_FALL = 200,
DAMAGE_BITE = 200,
DAMAGE_ATTACK = 400
};
enum {
@@ -941,7 +970,7 @@ struct Bear : Enemy
case STATE_DEATH:
if ((flags & ITEM_FLAG_REVERSE) && (hitMask & HIT_MASK) && tinfo.target) // fall on Lara
{
tinfo.target->hit(200, _vec3i(0, 0, 0), 0);
tinfo.target->hit(DAMAGE_FALL, _vec3i(0, 0, 0), 0);
flags &= ~ITEM_FLAG_REVERSE;
}
return STATE_DEATH;
@@ -961,10 +990,11 @@ struct Bear : Enemy
switch (state)
{
case STATE_WALK: {
case STATE_WALK:
{
extraE->maxTurn = ENEMY_TURN_2;
if (tinfo.target->health <= 0 && tinfo.aim && (hitMask & HIT_MASK))
if (tinfo.target->health <= 0 && tinfo.front && (hitMask & HIT_MASK))
return nextState = STATE_STOP; // eat lara! >:E
if (mood != MOOD_SLEEP)
@@ -982,19 +1012,23 @@ struct Bear : Enemy
}
break;
}
case STATE_STOP: {
case STATE_STOP:
{
if (tinfo.target->health <= 0)
return (tinfo.canAttack && tinfo.dist < 768) ? STATE_EAT : STATE_WALK;
if (nextState != STATE_NONE)
if (nextState)
return nextState;
return (mood == MOOD_SLEEP) ? STATE_WALK : STATE_RUN;
}
case STATE_HIND: {
case STATE_HIND:
{
if (flags & ITEM_FLAG_INJURED) {
nextState = STATE_NONE;
return STATE_HOWL;
}
if (tinfo.aim && (hitMask & HIT_MASK))
if (tinfo.front && (hitMask & HIT_MASK))
return STATE_HOWL;
if (mood == MOOD_ESCAPE) {
nextState = STATE_NONE;
@@ -1010,7 +1044,9 @@ struct Bear : Enemy
}
return STATE_HOWL;
}
case STATE_RUN: {
case STATE_RUN:
{
extraE->maxTurn = ENEMY_TURN_5;
if (hitMask & HIT_MASK) {
@@ -1023,7 +1059,7 @@ struct Bear : Enemy
if (tinfo.target->health <= 0 || mood == MOOD_SLEEP)
return STATE_STOP;
if (tinfo.aim && nextState == STATE_NONE)
if (!nextState && tinfo.front)
{
if (tinfo.dist < 2048)
{
@@ -1038,14 +1074,16 @@ struct Bear : Enemy
}
break;
}
case STATE_HOWL: {
case STATE_HOWL:
{
if (flags & ITEM_FLAG_INJURED)
{
nextState = STATE_NONE;
return STATE_STOP;
}
if (nextState != STATE_NONE)
if (nextState)
return nextState;
if (mood == MOOD_SLEEP || mood == MOOD_ESCAPE)
@@ -1056,16 +1094,20 @@ struct Bear : Enemy
return STATE_HIND;
}
case STATE_BITE: {
if ((nextState == STATE_NONE) && (hitMask & HIT_MASK)) {
bite(14, _vec3i(0, 96, 335), 200);
case STATE_BITE:
{
if (!nextState && (hitMask & HIT_MASK)) {
bite(DAMAGE_BITE, _vec3i(0, 96, 335), 14);
nextState = STATE_STOP;
}
break;
}
case STATE_ATTACK: {
if ((nextState == STATE_NONE) && (hitMask & HIT_MASK) && tinfo.target) {
tinfo.target->hit(400, _vec3i(0, 0, 0), 0);
case STATE_ATTACK:
{
if (!nextState && (hitMask & HIT_MASK) && tinfo.target) {
tinfo.target->hit(DAMAGE_ATTACK, _vec3i(0, 0, 0), 0);
nextState = STATE_HOWL;
}
break;
@@ -1079,6 +1121,10 @@ struct Bear : Enemy
struct Bat : Enemy
{
enum {
DAMAGE_ATTACK = 2
};
enum {
STATE_NONE,
STATE_AWAKE,
@@ -1101,9 +1147,8 @@ struct Bat : Enemy
flags &= ~ITEM_FLAG_GRAVITY;
}
if (flags & ITEM_FLAG_GRAVITY) {
if (flags & ITEM_FLAG_GRAVITY)
return STATE_CIRCLING;
}
pos.y = roomFloor;
return STATE_DEATH;
@@ -1112,19 +1157,26 @@ struct Bat : Enemy
switch (state)
{
case STATE_AWAKE:
{
return STATE_FLY;
}
case STATE_FLY:
if (hitMask) {
{
if (hitMask)
return STATE_ATTACK;
}
break;
}
case STATE_ATTACK:
{
if (!hitMask) {
mood = MOOD_SLEEP;
return STATE_FLY;
}
bite(4, _vec3i(0, 16, 45), 2);
bite(DAMAGE_ATTACK, _vec3i(0, 16, 45), 4);
break;
}
}
return goalState;
@@ -1207,7 +1259,116 @@ struct Rat : Enemy
struct Rex : Enemy
{
enum {
HIT_MASK = 0x00003000, // head
DIST_BITE = 1500,
DIST_WALK = 4096,
DIST_RUN = 5120,
DAMAGE_RUN = 10,
DAMAGE_WALK = 1,
DAMAGE_FATAL = 1000
};
enum {
STATE_NONE,
STATE_STOP,
STATE_WALK,
STATE_RUN,
STATE_UNUSED,
STATE_DEATH,
STATE_ROAR,
STATE_BITE,
STATE_FATAL
};
Rex(Room* room) : Enemy(room, 100, 341, 2000, AGGRESSION_LVL_MAX) {}
virtual int32 updateState()
{
if (health <= 0) {
return (state == STATE_STOP) ? STATE_DEATH : STATE_STOP;
}
extraE->rotNeck = (extraE->rotHead >>= 1);
if (hitMask)
{
tinfo.target->hit((state == STATE_RUN) ? DAMAGE_RUN : DAMAGE_WALK, _vec3i(0, 0, 0), 0);
}
bool walk = (tinfo.canAttack && (tinfo.dist > DIST_BITE) && (tinfo.dist < DIST_WALK)) ||
(tinfo.behind && !tinfo.front && (mood != MOOD_ESCAPE));
switch (state)
{
case STATE_STOP:
{
if (nextState)
return nextState;
if (tinfo.canAttack && (tinfo.dist < DIST_BITE))
return STATE_BITE;
if ((mood == MOOD_SLEEP) || walk)
return STATE_WALK;
return STATE_RUN;
}
case STATE_WALK:
{
extraE->maxTurn = ENEMY_TURN_2;
if ((mood != MOOD_SLEEP) && !walk)
return STATE_STOP;
if (tinfo.front && (rand_logic() < 0x200))
{
nextState = STATE_ROAR;
return STATE_STOP;
}
break;
}
case STATE_RUN:
{
extraE->maxTurn = ENEMY_TURN_4;
if (((tinfo.dist < DIST_RUN) && tinfo.canAttack) || walk)
return STATE_STOP;
if ((mood != MOOD_ESCAPE) && tinfo.front && (rand_logic() < 0x200))
{
nextState = STATE_ROAR;
return STATE_STOP;
}
if (mood == MOOD_SLEEP)
return STATE_STOP;
break;
}
case STATE_BITE:
{
nextState = STATE_WALK;
if (hitMask & HIT_MASK)
{
angle.x = 0;
angle.z = 0;
tinfo.target->pos = pos;
tinfo.target->angle = angle;
tinfo.target->flags &= ~ITEM_FLAG_GRAVITY;
tinfo.target->hit(DAMAGE_FATAL, _vec3i(0, 0, 0), 0);
tinfo.target->meshSwap(ITEM_LARA_SPEC, 0xFFFFFFFF);
tinfo.target->animSet(level.models[ITEM_LARA_SPEC].animIndex + 1, true);
if (tinfo.target->room != room)
{
tinfo.target->room->remove(tinfo.target);
room->add(tinfo.target);
}
return STATE_FATAL;
}
break;
}
}
return goalState;
}
};
@@ -1215,11 +1376,12 @@ struct Raptor : Enemy
{
enum {
HIT_MASK = 0xFF7C00, // hands and head
DIST_BITE = 680,
DIST_ATTACK = 1536
};
enum {
DIST_BITE = 680,
DIST_ATTACK = 1536,
DAMAGE_ATTACK = 100,
ANIM_DEATH_1 = 9,
ANIM_DEATH_2 = 10
};
@@ -1256,7 +1418,7 @@ struct Raptor : Enemy
switch (state)
{
case STATE_STOP: {
if (nextState != STATE_NONE)
if (nextState)
return nextState;
if ((hitMask & HIT_MASK) || (tinfo.canAttack && tinfo.dist < DIST_BITE))
return STATE_BITE;
@@ -1266,6 +1428,7 @@ struct Raptor : Enemy
return STATE_WALK;
return STATE_RUN;
}
case STATE_WALK: {
extraE->maxTurn = ENEMY_TURN_1;
tinfo.tilt = tinfo.turn >> 1;
@@ -1279,6 +1442,7 @@ struct Raptor : Enemy
}
break;
}
case STATE_RUN: {
extraE->maxTurn = ENEMY_TURN_4;
tinfo.tilt = tinfo.turn;
@@ -1293,7 +1457,7 @@ struct Raptor : Enemy
return (rand_logic() < 0x2000) ? STATE_STOP : STATE_ATTACK_2;
}
if (tinfo.aim && (mood != MOOD_ESCAPE) && (rand_logic() < 0x100))
if (tinfo.front && (mood != MOOD_ESCAPE) && (rand_logic() < 0x100))
{
nextState = STATE_ROAR;
return STATE_STOP;
@@ -1303,13 +1467,14 @@ struct Raptor : Enemy
return STATE_STOP;
break;
}
case STATE_ATTACK_1:
case STATE_ATTACK_2:
case STATE_BITE:
{
tinfo.tilt = tinfo.turn;
if ((nextState == STATE_NONE) && tinfo.aim && (hitMask & HIT_MASK)) {
bite(22, _vec3i(0, 66, 318), 100);
if (!nextState && tinfo.front && (hitMask & HIT_MASK)) {
bite(DAMAGE_ATTACK, _vec3i(0, 66, 318), 22);
nextState = (state == STATE_ATTACK_2) ? STATE_RUN : STATE_STOP;
}
break;

View File

@@ -221,6 +221,7 @@ void gameLoadLevel(const void* data)
//resetLara(0, 44, _vec3i(27868, -1024, 29191), -ANGLE_90); // swing blades
// level 3a
//resetLara(0, 44, _vec3i(73798, 2304, 9819), ANGLE_90); // uw gears
//resetLara(0, 51, _vec3i(41015, 3584, 34494), ANGLE_180); // valley
}
drawLevelInit();

View File

@@ -1404,6 +1404,18 @@ uint32 ItemObj::updateHitMask(Lara* lara, CollisionInfo* cinfo)
return hitMask;
}
void ItemObj::meshSwap(ItemType type, uint32 mask)
{
int32 start = level.models[type].start;
for (int32 i = 0; i < JOINT_MAX && mask; i++, mask >>= 1)
{
if (mask & 1) {
extraL->meshes[i] = start + i;
}
}
}
ItemObj* ItemObj::init(Room* room)
{
#define INIT_ITEM(type, className) case ITEM_##type : return new (this) className(room)

View File

@@ -3933,18 +3933,6 @@ struct Lara : ItemObj
}
}
void meshSwap(ItemType type, uint32 mask)
{
int32 start = level.models[type].start;
for (int32 i = 0; i < JOINT_MAX && mask; i++, mask >>= 1)
{
if (mask & 1) {
extraL->meshes[i] = start + i;
}
}
}
void meshSwapPistols(uint32 weaponMask, uint32 bodyMask)
{
const WeaponParams &params = weaponParams[extraL->weapon];