1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-13 16:44:50 +02:00

#14 basic behavior of wolves

This commit is contained in:
XProger
2016-11-17 03:57:40 +03:00
parent f9324e6543
commit 7f004415e1
4 changed files with 243 additions and 55 deletions

View File

@@ -41,9 +41,13 @@ struct Controller {
int *meshes;
int mCount;
// TODO: Character class
quat *animOverrides; // left & right arms animation frames
int animOverrideMask;
mat4 *joints;
int health;
float tilt;
struct ActionCommand {
TR::Action action;
@@ -64,6 +68,8 @@ struct Controller {
animPrev = animIndex;
state = level->anims[animIndex].state;
TR::Model &model = getModel();
health = 100;
tilt = 0.0f;
}
virtual ~Controller() {
@@ -78,6 +84,10 @@ struct Controller {
meshes = mCount ? new int[mCount] : NULL;
for (int i = 0; i < mCount; i++)
meshes[i] = model.mStart + i;
}
void initAnimOverrides() {
TR::Model &model = getModel();
animOverrides = new quat[model.mCount];
animOverrideMask = 0;
@@ -181,6 +191,36 @@ struct Controller {
return matrix;
}
bool aim(int target, int joint, const vec4 &angleRange, quat &rot, quat *rotAbs = NULL) {
if (target > -1) {
TR::Entity &e = level->entities[target];
Box box = ((Controller*)e.controller)->getBoundingBox();
vec3 t = (box.min + box.max) * 0.5f;
mat4 m = getJoint(joint);
vec3 delta = (m.inverse() * t).normal();
float angleY = clampAngle(atan2(delta.x, delta.z));
float angleX = clampAngle(asinf(delta.y));
if (angleX > angleRange.x && angleX <= angleRange.y &&
angleY > angleRange.z && angleY <= angleRange.w) {
quat ax(vec3(1, 0, 0), -angleX);
quat ay(vec3(0, 1, 0), angleY);
rot = ay * ax;
if (rotAbs)
*rotAbs = m.getRot() * rot;
return true;
}
}
if (rotAbs)
*rotAbs = rotYXZ(angle);
return false;
}
void updateEntity() {
TR::Entity &e = getEntity();
e.x = int(pos.x);
@@ -764,6 +804,10 @@ struct Controller {
renderShadow(mesh, vec3(entity.x, info.floor - 16.0f, entity.z), (bmax + bmin) * 0.5f, (bmax - bmin) * 0.8f, entity.rotation);
}
}
quat lerpFrames(TR::AnimFrame *frameA, TR::AnimFrame *frameB, float t, int index) {
return lerpAngle(frameA->getAngle(index), frameB->getAngle(index), t);
}
};

View File

@@ -4,9 +4,15 @@
#include "controller.h"
struct Enemy : Controller {
int health;
int target;
quat rotHead, rotChest;
int baseAnim;
Enemy(TR::Level *level, int entity) : Controller(level, entity), health(100) {}
Enemy(TR::Level *level, int entity) : Controller(level, entity), target(-1) {
initAnimOverrides();
rotHead = rotChest = quat(0, 0, 0, 1);
baseAnim = animIndex;
}
virtual Stand getStand() {
return STAND_GROUND;
@@ -16,11 +22,128 @@ struct Enemy : Controller {
health -= damage;
};
virtual bool activate(ActionCommand *cmd) {
Controller::activate(cmd);
getEntity().flags.active = true;
activateNext();
for (int i = 0; i < level->entitiesCount; i++)
if (level->entities[i].type == TR::Entity::LARA) {
target = i;
break;
}
return true;
}
virtual void updateVelocity() {
TR::Animation *anim = &level->anims[animIndex];
float speed = anim->speed + anim->accel * (animTime * 30.0f);
velocity = getDir() * speed;
}
virtual void move() {
if (!getEntity().flags.active) return;
vec3 p = pos;
pos += velocity * Core::deltaTime * 30.0f;
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), (int)pos.x, (int)pos.z, info);
if (pos.y - info.floor > 1024) {
pos = p;
return;
}
if (stand == STAND_GROUND)
pos.y = info.floor;
updateEntity();
checkRoom();
}
virtual void checkRoom() {
TR::Level::FloorInfo info;
TR::Entity &e = getEntity();
level->getFloorInfo(e.room, e.x, e.z, info);
if (info.roomNext != 0xFF)
e.room = info.roomNext;
if (info.roomBelow != 0xFF && e.y > info.floor)
e.room = info.roomBelow;
if (info.roomAbove != 0xFF && e.y <= info.ceiling) {
if (stand == STAND_UNDERWATER && !level->rooms[info.roomAbove].flags.water) {
stand = STAND_ONWATER;
velocity.y = 0;
pos.y = info.ceiling;
updateEntity();
} else
if (stand != STAND_ONWATER)
e.room = info.roomAbove;
}
}
void setOverrides(bool active, int chest, int head) {
int mask = 0;
if (head > -1) mask |= (1 << head);
if (chest > -1) mask |= (1 << chest);
if (active)
animOverrideMask |= mask;
else
animOverrideMask &= ~mask;
TR::AnimFrame *frameA, *frameB;
float t;
getFrames(&frameA, &frameB, t, animIndex, animTime, true);
animOverrides[chest] = lerpFrames(frameA, frameB, t, chest);
animOverrides[head] = lerpFrames(frameA, frameB, t, head);
}
void lookAt(int target, int chest, int head) {
float speed = 8.0f * Core::deltaTime;
quat rot;
if (chest > -1) {
if (aim(target, chest, vec4(-PI * 0.4f, PI * 0.4f, -PI * 0.75f, PI * 0.75f), rot))
rotChest = rotChest.slerp(quat(0, 0, 0, 1).slerp(rot, 0.5f), speed);
else
rotChest = rotChest.slerp(quat(0, 0, 0, 1), speed);
animOverrides[chest] = rotChest * animOverrides[chest];
}
if (head > -1) {
if (aim(target, head, vec4(-PI * 0.25f, PI * 0.25f, -PI * 0.5f, PI * 0.5f), rot))
rotHead = rotHead.slerp(rot, speed);
else
rotHead = rotHead.slerp(quat(0, 0, 0, 1), speed);
animOverrides[head] = rotHead * animOverrides[head];
}
}
virtual int getInputMask() {
if (target > -1) {
vec3 v = (((Controller*)level->entities[target].controller)->pos - pos).normal();
float d = atan2(v.x, v.z) - angle.y;
if (fabsf(d) > 0.01f)
return d < 0 ? LEFT : RIGHT;
}
return 0;
}
};
#define WOLF_TURN_FAST PI
#define WOLF_TURN_SLOW (PI / 3.0f)
struct Wolf : Enemy {
enum {
ANIM_DEATH = 20,
ANIM_DEATH_RUN = 21,
ANIM_DEATH_JUMP = 22,
};
enum {
STATE_STOP = 1,
STATE_WALK = 2,
@@ -31,9 +154,15 @@ struct Wolf : Enemy {
STATE_SLEEP = 8,
STATE_GROWL = 9,
STATE_10 = 10, // WTF?
STATE_DEATH = 11,
STATE_ATTACK = 12,
};
enum {
JOINT_CHEST = 2,
JOINT_HEAD = 3
};
Wolf(TR::Level *level, int entity) : Enemy(level, entity) {}
virtual int getStateGround() {
@@ -45,13 +174,67 @@ struct Wolf : Enemy {
// STATE_JUMP -> STATE_RUN
// STATE_GROWL -> STATE_STOP, STATE_RUN, STATE_STALKING, STATE_HOWL, STATE_ATTACK
// STATE_BITING -> NULL
if (state == STATE_DEATH) return state;
if (health <= 0) {
switch (state) {
case STATE_RUN : return setAnimation(baseAnim + ANIM_DEATH_RUN);
case STATE_JUMP : return setAnimation(baseAnim + ANIM_DEATH_JUMP);
default : return setAnimation(baseAnim + ANIM_DEATH);
}
}
TR::Entity &e = getEntity();
if (!e.flags.active)
return (state == STATE_STOP || state == STATE_SLEEP) ? STATE_SLEEP : STATE_STOP;
switch (state) {
case STATE_SLEEP : return STATE_STOP;
case STATE_STOP : return STATE_HOWL;
case STATE_GROWL : return STATE_STALKING;
case STATE_STALKING : if (health < 70) return STATE_RUN; break;
}
if (target > -1 && (state == STATE_STALKING || state == STATE_RUN)) {
vec3 v = ((Controller*)level->entities[target].controller)->pos - pos;
float d = v.length();
if (state == STATE_STALKING && d < 512)
return STATE_ATTACK;
if (state == STATE_RUN && d > 512 && d < 1024)
return STATE_JUMP;
}
if (state == STATE_JUMP)
return STATE_RUN;
// if (state == STATE_SLEEP) return STATE_STOP;
// if (state == STATE_STOP) return STATE_GROWL;
// if (state == STATE_GROWL) return STATE_ATTACK;
// if (state == STATE_RUN) return STATE_10;
return state;
}
virtual void updateState() {
Enemy::updateState();
float w = 0.0f;
if (state == STATE_RUN || state == STATE_STALKING) {
w = state == STATE_RUN ? WOLF_TURN_FAST : WOLF_TURN_SLOW;
if (mask & LEFT) w = -w;
if (w != 0.0f) {
w *= Core::deltaTime;
angle.y += w;
velocity = velocity.rotateY(-w);
}
} else
velocity = vec3(0.0f);
}
virtual void move() {
if (state == STATE_DEATH) {
animOverrideMask = 0;
return;
}
Enemy::move();
setOverrides(state == STATE_STALKING || state == STATE_RUN, JOINT_CHEST, JOINT_HEAD);
lookAt(target, JOINT_CHEST, JOINT_HEAD);
}
};

View File

@@ -194,11 +194,9 @@ struct Lara : Controller {
int target;
quat rotHead, rotChest;
int health;
float tilt;
Lara(TR::Level *level, int entity) : Controller(level, entity), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos), target(-1), health(100), tilt(0.0f) {
Lara(TR::Level *level, int entity) : Controller(level, entity), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos), target(-1) {
initMeshOverrides();
initAnimOverrides();
for (int i = 0; i < 2; i++) {
arms[i].shotTimer = MUZZLE_FLASH_TIME + 1.0f;
arms[i].animTime = 0.0f;
@@ -295,7 +293,7 @@ struct Lara : Controller {
int wpnGetDamage() {
switch (wpnCurrent) {
case Weapon::PISTOLS : return 10;
case Weapon::SHOTGUN : return 5;
case Weapon::SHOTGUN : return 15;
case Weapon::MAGNUMS : return 20;
case Weapon::UZIS : return 5;
default : return 0;
@@ -741,35 +739,6 @@ struct Lara : Controller {
}
}
bool aim(int target, int joint, const vec4 &angleRange, quat &rot, quat *rotAbs = NULL) {
if (target > -1) {
TR::Entity &e = level->entities[target];
vec3 t(e.x, e.y, e.z);
mat4 m = getJoint(joint);
vec3 delta = (m.inverse() * t).normal();
float angleY = clampAngle(atan2(delta.x, delta.z));
float angleX = clampAngle(asinf(delta.y));
if (angleX > angleRange.x && angleX <= angleRange.y &&
angleY > angleRange.z && angleY <= angleRange.w) {
quat ax(vec3(1, 0, 0), -angleX);
quat ay(vec3(0, 1, 0), angleY);
rot = ay * ax;
if (rotAbs)
*rotAbs = m.getRot() * rot;
return true;
}
}
if (rotAbs)
*rotAbs = rotYXZ(angle);
return false;
}
void updateTargets() {
if (emptyHands() || !wpnReady()) {
target = arms[0].target = arms[1].target = -1;
@@ -795,11 +764,13 @@ struct Lara : Controller {
int index = -1;
for (int i = 0; i < level->entitiesCount; i++) {
TR::Entity &e = level->entities[i];
if (!e.flags.rendered || !e.isEnemy()) continue;
if (!e.flags.active || !e.isEnemy()) continue;
Controller *controller = (Controller*)e.controller;
if (controller->health <= 0) continue;
vec3 p = vec3(e.x, e.y, e.z);
vec3 p = controller->pos;
vec3 v = p - pos;
if (dir.dot(v.normal()) <= 0.5f) continue; // target is out of sigth -60..+60 degrees
if (dir.dot(v.normal()) <= 0.5f) continue; // target is out of sight -60..+60 degrees
int d = v.length();
if (d < dist && checkOcclusion(pos - vec3(0, 512, 0), p, d) ) {
@@ -1490,22 +1461,12 @@ struct Lara : Controller {
updateWeapon();
}
quat lerpFrames(TR::AnimFrame *frameA, TR::AnimFrame *frameB, float t, int index) {
return lerpAngle(frameA->getAngle(index), frameB->getAngle(index), t);
}
virtual void updateVelocity() {
// calculate moving speed
float dt = Core::deltaTime * 30.0f;
TR::Animation *anim = &level->anims[animIndex];
//if (anim->speed != 0.0f || anim->accel != 0.0f)
// LOG("speed: %f accel: %f\n", (float)anim->speed, (float)anim->accel);
switch (stand) {
case STAND_AIR :
velocity.y += GRAVITY * dt;
velocity.y += GRAVITY * 30.0f * Core::deltaTime;
break;
case STAND_GROUND :
case STAND_SLIDE :

View File

@@ -20,7 +20,7 @@
#endif
#define SND_CHANNELS_MAX 32
#define SND_FADEOFF_DIST (1024.0f * 5.0f)
#define SND_FADEOFF_DIST (1024.0f * 10.0f)
namespace Sound {