From d91a919f56d3296b238e5a2b5b9c8448ee4775cd Mon Sep 17 00:00:00 2001 From: XProger Date: Sat, 7 May 2022 04:49:04 +0300 Subject: [PATCH] #368 GBA T-Rex AI --- src/fixed/camera.h | 9 +- src/fixed/common.h | 20 +++- src/fixed/enemy.h | 289 +++++++++++++++++++++++++++++++++++---------- src/fixed/game.h | 1 + src/fixed/item.h | 12 ++ src/fixed/lara.h | 12 -- 6 files changed, 264 insertions(+), 79 deletions(-) diff --git a/src/fixed/camera.h b/src/fixed/camera.h index b0bf2e7e..4761c412 100644 --- a/src/fixed/camera.h +++ b/src/fixed/camera.h @@ -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) diff --git a/src/fixed/common.h b/src/fixed/common.h index a893ed62..84338b43 100644 --- a/src/fixed/common.h +++ b/src/fixed/common.h @@ -154,7 +154,20 @@ #endif #ifdef _DEBUG - #define LOG(...) printf(__VA_ARGS__) + #if defined(__WIN32__) + #include + 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; }; diff --git a/src/fixed/enemy.h b/src/fixed/enemy.h index ad52505c..4425b7a1 100644 --- a/src/fixed/enemy.h +++ b/src/fixed/enemy.h @@ -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 - }; + DIST_ATTACK = 1536, + + DAMAGE_ATTACK = 100, - enum { 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; diff --git a/src/fixed/game.h b/src/fixed/game.h index f19e3c40..2b907cfe 100644 --- a/src/fixed/game.h +++ b/src/fixed/game.h @@ -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(); diff --git a/src/fixed/item.h b/src/fixed/item.h index 2e4c7d46..fda23467 100644 --- a/src/fixed/item.h +++ b/src/fixed/item.h @@ -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) diff --git a/src/fixed/lara.h b/src/fixed/lara.h index b170562a..fad5a0ce 100644 --- a/src/fixed/lara.h +++ b/src/fixed/lara.h @@ -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 ¶ms = weaponParams[extraL->weapon];