mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-17 11:00:38 +02:00
Initial Prey Game integration support.
This commit is contained in:
@@ -0,0 +1,801 @@
|
||||
|
||||
|
||||
#include "precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "prey_local.h"
|
||||
|
||||
const idEventDef EV_AcidBlast("acidBlast");
|
||||
const idEventDef EV_AcidDrip("<acidDrip>");
|
||||
const idEventDef EV_DeathCloud("<deathCloud>");
|
||||
const idEventDef EV_LaunchPod("launchPod");
|
||||
const idEventDef EV_NewPod("<newPod>", "e");
|
||||
const idEventDef EV_SpawnBlastDebris("<spawnBlastDebris>");
|
||||
const idEventDef EV_ChargeEnemy("chargeEnemy");
|
||||
const idEventDef EV_GrabEnemy("grabEnemy", NULL, 'f');
|
||||
const idEventDef EV_BiteEnemy("biteEnemy");
|
||||
const idEventDef EV_DirectMoveToPosition("directMoveToPosition", "v");
|
||||
const idEventDef EV_BindUnfroze("<bindUnfroze>", "e");
|
||||
const idEventDef EV_GrabCheck("grabCheck", "", 'd');
|
||||
const idEventDef EV_MoveToGrabPosition("moveToGrabPosition");
|
||||
const idEventDef EV_CheckRange("checkRange", "", 'd');
|
||||
const idEventDef EV_EnemyRangeZ("enemyRangeZ", "", 'f');
|
||||
|
||||
CLASS_DECLARATION(hhMonsterAI, hhGasbagSimple)
|
||||
EVENT(EV_AcidBlast, hhGasbagSimple::Event_AcidBlast)
|
||||
EVENT(EV_AcidDrip, hhGasbagSimple::Event_AcidDrip)
|
||||
EVENT(EV_DeathCloud, hhGasbagSimple::Event_DeathCloud)
|
||||
EVENT(EV_LaunchPod, hhGasbagSimple::Event_LaunchPod)
|
||||
EVENT(EV_NewPod, hhGasbagSimple::Event_NewPod)
|
||||
EVENT(EV_SpawnBlastDebris, hhGasbagSimple::Event_SpawnBlastDebris)
|
||||
EVENT(EV_ChargeEnemy, hhGasbagSimple::Event_ChargeEnemy)
|
||||
EVENT(EV_GrabEnemy, hhGasbagSimple::Event_GrabEnemy)
|
||||
EVENT(EV_BiteEnemy, hhGasbagSimple::Event_BiteEnemy)
|
||||
EVENT(EV_DirectMoveToPosition, hhGasbagSimple::Event_DirectMoveToPosition)
|
||||
EVENT(EV_BindUnfroze, hhGasbagSimple::Event_BindUnfroze)
|
||||
EVENT(EV_GrabCheck, hhGasbagSimple::Event_GrabCheck)
|
||||
EVENT(EV_MoveToGrabPosition, hhGasbagSimple::Event_MoveToGrabPosition)
|
||||
EVENT(EV_CheckRange, hhGasbagSimple::Event_CheckRange)
|
||||
EVENT(EV_EnemyRangeZ, hhGasbagSimple::Event_EnemyRangeZ)
|
||||
END_CLASS
|
||||
|
||||
static const idEventDef EV_Unfreeze( "<unfreeze>" );
|
||||
|
||||
#ifndef ID_DEMO_BUILD //HUMANHEAD jsh PCF 5/26/06: code removed for demo build
|
||||
//
|
||||
// ~hhGasbagSimple()
|
||||
//
|
||||
hhGasbagSimple::~hhGasbagSimple(void) {
|
||||
// Make sure to unbind the player if we've grabbed him to prevent deleting him
|
||||
if (enemy.IsValid() && enemy->IsBoundTo(this) && enemy->IsType(hhPlayer::Type)) {
|
||||
enemy->Unbind();
|
||||
enemy->PostEventMS(&EV_Unfreeze, 0);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Spawn()
|
||||
//
|
||||
void hhGasbagSimple::Spawn(void) {
|
||||
Event_SetMoveType(MOVETYPE_FLY); // So we ignore gravity zones from the beginning
|
||||
podOffset = spawnArgs.GetVector("pod_offset", "0 0 64");
|
||||
podRange = spawnArgs.GetFloat("podRange", "250");
|
||||
dripCount = 0;
|
||||
nextWoundTime = 0;
|
||||
|
||||
bindController = static_cast<hhBindController *>( gameLocal.SpawnClientObject(spawnArgs.GetString("def_bindController"), NULL) );
|
||||
HH_ASSERT( bindController.IsValid() );
|
||||
bindController->fl.networkSync = false;
|
||||
|
||||
float yawLimit = spawnArgs.GetFloat("yawlimit", "180");
|
||||
const char *handName = spawnArgs.GetString("def_tractorhand");
|
||||
const char *animName = spawnArgs.GetString("boundanim");
|
||||
|
||||
bindController->SetRiderParameters(animName, handName, yawLimit, 0.0f);
|
||||
bindController->Bind(this, true);
|
||||
bindController->SetOrigin(spawnArgs.GetVector("grab_offset"));
|
||||
bindController->fl.neverDormant = true;
|
||||
}
|
||||
|
||||
//
|
||||
// Ticker()
|
||||
//
|
||||
void hhGasbagSimple::Ticker(void) {
|
||||
for (int i = 0; i < podList.Num(); i++) {
|
||||
if (!podList[i].IsValid()) {
|
||||
podList.RemoveIndex(i);
|
||||
i--; // This index is gone, so don't increment
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the podcount
|
||||
AI_PODCOUNT = podList.Num();
|
||||
|
||||
if (podList.Num() == 0) {
|
||||
BecomeInactive(TH_TICKER);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Save()
|
||||
//
|
||||
void hhGasbagSimple::Save(idSaveGame *savefile) const {
|
||||
savefile->WriteInt(dripCount);
|
||||
|
||||
savefile->WriteInt(podList.Num());
|
||||
for (int i = 0; i < podList.Num(); i++) {
|
||||
podList[i].Save(savefile);
|
||||
}
|
||||
bindController.Save(savefile);
|
||||
}
|
||||
|
||||
//
|
||||
// Restore()
|
||||
//
|
||||
void hhGasbagSimple::Restore(idRestoreGame *savefile) {
|
||||
Spawn();
|
||||
savefile->ReadInt(dripCount);
|
||||
|
||||
int num;
|
||||
savefile->ReadInt(num);
|
||||
podList.SetNum(num);
|
||||
for (int i = 0; i < num; i++) {
|
||||
podList[i].Restore(savefile);
|
||||
}
|
||||
bindController.Restore(savefile);
|
||||
|
||||
nextWoundTime = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Event_AcidBlast()
|
||||
//
|
||||
void hhGasbagSimple::Event_AcidBlast(void) {
|
||||
if (health <= 0) { // Don't launch a pod if we're dead
|
||||
return;
|
||||
}
|
||||
|
||||
if (!enemy.IsValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
idEntity *target = enemy.GetEntity();
|
||||
idVec3 targetOrigin = target->GetOrigin();
|
||||
float len, nearest = podRange;
|
||||
|
||||
// See if we have a pod near the enemy. If we do, target it instead.
|
||||
idEntity *entList[100];
|
||||
int num = gameLocal.EntitiesWithinRadius(targetOrigin, podRange, entList, 100);
|
||||
for (int i = 0; i < num; i++) {
|
||||
if (!entList[i]->IsType(hhPod::Type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
len = (entList[i]->GetOrigin() - targetOrigin).Length();
|
||||
|
||||
// Always target the pod nearest to the enemy
|
||||
if (len < nearest) {
|
||||
target = entList[i];
|
||||
nearest = len;
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn a "muzzle flash"-like fx
|
||||
hhFxInfo fx;
|
||||
fx.SetEntity(this);
|
||||
fx.RemoveWhenDone(true);
|
||||
SpawnFxLocal(spawnArgs.GetString("fx_fire"), GetOrigin() + idVec3(0, 0, -400), enemy->GetPhysics()->GetGravityNormal().ToMat3(), &fx);
|
||||
|
||||
// Spawn an acid blob in the player's general direction
|
||||
const idDict *projDef = gameLocal.FindEntityDefDict(spawnArgs.GetString("def_projectile"), false);
|
||||
LaunchProjectile(spawnArgs.GetString("acidbone", "LmbRt"), target, false, projDef);
|
||||
|
||||
PostEventMS(&EV_SpawnBlastDebris, 100 + gameLocal.random.RandomInt(100));
|
||||
|
||||
dripCount = gameLocal.random.RandomInt(3) + 1;
|
||||
PostEventMS(&EV_AcidDrip, 200 + gameLocal.random.RandomInt(100)); // Drip acid after firing
|
||||
}
|
||||
|
||||
//
|
||||
// Event_SpawnBlastDebris()
|
||||
//
|
||||
void hhGasbagSimple::Event_SpawnBlastDebris(void) {
|
||||
idDict args;
|
||||
args.Clear();
|
||||
args.Set("origin", (GetPhysics()->GetOrigin() + spawnArgs.GetVector("blast_debris_offset", "0 0 128")).ToString());
|
||||
gameLocal.SpawnObject("debrisSpawner_gasbag_fire", &args);
|
||||
}
|
||||
|
||||
//
|
||||
// Event_AcidDrip()
|
||||
//
|
||||
void hhGasbagSimple::Event_AcidDrip(void) {
|
||||
assert(dripCount > 0);
|
||||
dripCount--;
|
||||
|
||||
idVec3 target = GetPhysics()->GetOrigin();
|
||||
target.z -= 128;
|
||||
|
||||
idVec3 rndVec = hhUtils::RandomVector() * gameLocal.random.RandomInt(20);
|
||||
const idDict *projDef = gameLocal.FindEntityDefDict(spawnArgs.GetString("def_drip_projectile", "projectile_acidspray_gasbag"), false);
|
||||
LaunchProjectileAtVec(spawnArgs.GetString("acidbone", "LmbRt"), target + rndVec, false, projDef);
|
||||
|
||||
if (dripCount > 0) {
|
||||
PostEventMS(&EV_AcidDrip, 200 + gameLocal.random.RandomInt(100)); // Fire again in 200-300ms
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Event_DeathCloud()
|
||||
//
|
||||
void hhGasbagSimple::Event_DeathCloud(void) {
|
||||
const char *fx = spawnArgs.GetString("fx_death");
|
||||
if (!fx || !fx[0]) {
|
||||
return;
|
||||
}
|
||||
hhFxInfo fxInfo;
|
||||
|
||||
fxInfo.RemoveWhenDone(true);
|
||||
BroadcastFxInfo(fx, GetOrigin(), GetAxis(), &fxInfo);
|
||||
}
|
||||
|
||||
//
|
||||
// Event_LaunchPod()
|
||||
//
|
||||
void hhGasbagSimple::Event_LaunchPod() {
|
||||
if (health <= 0) { // Don't launch a pod if we're dying
|
||||
return;
|
||||
}
|
||||
|
||||
idVec3 target, origin, offset = vec3_origin;
|
||||
if (enemy.IsValid()) {
|
||||
// Target a random area around the enemy
|
||||
origin = enemy->GetEyePosition();
|
||||
if (gameLocal.random.RandomInt(100) > 30) { // Possibly target the enemy exactly
|
||||
offset.x = (gameLocal.random.RandomFloat() * 100.0f) - 50.0f;
|
||||
offset.y = (gameLocal.random.RandomFloat() * 100.0f) - 50.0f;
|
||||
}
|
||||
} else {
|
||||
// Target a random area beneath us
|
||||
origin = GetOrigin();
|
||||
offset.x = gameLocal.random.RandomFloat() * 10.0f;
|
||||
offset.y = gameLocal.random.RandomFloat() * 10.0f;
|
||||
offset.z = -10.0f;
|
||||
}
|
||||
target = origin + offset;
|
||||
|
||||
const idDict *projDef = gameLocal.FindEntityDefDict(spawnArgs.GetString("def_pod_projectile", "projectile_gasbag_pod"), false);
|
||||
LaunchProjectileAtVec(spawnArgs.GetString("acidbone", "LmbRt"), target, false, projDef);
|
||||
}
|
||||
|
||||
//
|
||||
// Event_NewPod
|
||||
//
|
||||
void hhGasbagSimple::Event_NewPod(hhPod *pod) {
|
||||
HH_ASSERT(pod && pod->IsType(hhPod::Type));
|
||||
|
||||
podList.Append(idEntityPtr<hhPod>(pod));
|
||||
|
||||
// Update the podcount
|
||||
AI_PODCOUNT = podList.Num();
|
||||
|
||||
BecomeActive(TH_TICKER);
|
||||
}
|
||||
|
||||
//
|
||||
// Killed
|
||||
//
|
||||
void hhGasbagSimple::Killed(idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location) {
|
||||
bool wasDead = (AI_DEAD != 0);
|
||||
|
||||
hhMonsterAI::Killed(inflictor, attacker, damage, dir, location);
|
||||
|
||||
if (wasDead) {
|
||||
return;
|
||||
}
|
||||
|
||||
CancelEvents(&EV_AcidDrip);
|
||||
CancelEvents(&EV_AcidBlast);
|
||||
CancelEvents(&EV_LaunchPod);
|
||||
dripCount = 0;
|
||||
|
||||
// Release any grabbed enemy
|
||||
idEntity *rider;
|
||||
if ((rider = bindController->GetRider()) != NULL) {
|
||||
bindController->Detach();
|
||||
if (rider->IsType(hhPlayer::Type)) {
|
||||
rider->ProcessEvent(&EV_Unfreeze);
|
||||
}
|
||||
}
|
||||
|
||||
int deathAnim = GetAnimator()->GetAnim("death");
|
||||
GetAnimator()->PlayAnim(ANIMCHANNEL_TORSO, deathAnim, gameLocal.time, 300.0f);
|
||||
int ttl = GetAnimator()->AnimLength(deathAnim);
|
||||
// Freeze
|
||||
physicsObj.EnableGravity(false);
|
||||
physicsObj.PutToRest();
|
||||
|
||||
SetSkinByName( spawnArgs.GetString( "skin_death" ) );
|
||||
SetDeformation(DEFORMTYPE_DEATHEFFECT, gameLocal.time + 2500, 8000); // starttime, duration
|
||||
PostEventSec( &EV_StartSound, 1.5f, "snd_acid", SND_CHANNEL_ANY, 1 );
|
||||
|
||||
// Spawn gas cloud
|
||||
PostEventMS(&EV_DeathCloud, ttl);
|
||||
PostEventMS(&EV_Dispose, 0);
|
||||
}
|
||||
|
||||
//
|
||||
// LaunchProjectile
|
||||
//
|
||||
idProjectile *hhGasbagSimple::LaunchProjectileAtVec( const char *jointname, const idVec3 &target, bool clampToAttackCone, const idDict* desiredProjectileDef ) { //HUMANHEAD mdc - added desiredProjectileDef for supporting multiple projs.
|
||||
idVec3 muzzle;
|
||||
idVec3 dir;
|
||||
idVec3 start;
|
||||
trace_t tr;
|
||||
idBounds projBounds;
|
||||
float distance;
|
||||
const idClipModel *projClip;
|
||||
float attack_accuracy;
|
||||
float attack_cone;
|
||||
float projectile_spread;
|
||||
float diff;
|
||||
float angle;
|
||||
float spin;
|
||||
idAngles ang;
|
||||
int num_projectiles;
|
||||
int i;
|
||||
idMat3 axis;
|
||||
idVec3 tmp;
|
||||
idProjectile *lastProjectile;
|
||||
|
||||
//HUMANHEAD mdc - added to support multiple projectiles
|
||||
if( desiredProjectileDef ) { //try to set our projectile to the desiredProjectile
|
||||
int projIndex = FindProjectileInfo( desiredProjectileDef );
|
||||
if( projIndex >= 0 ) {
|
||||
SetCurrentProjectile( projIndex );
|
||||
}
|
||||
}
|
||||
//HUMANHEAD END
|
||||
|
||||
|
||||
if ( !projectileDef ) {
|
||||
gameLocal.Warning( "%s (%s) doesn't have a projectile specified", name.c_str(), GetEntityDefName() );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
attack_accuracy = spawnArgs.GetFloat( "attack_accuracy", "7" );
|
||||
attack_cone = spawnArgs.GetFloat( "attack_cone", "70" );
|
||||
projectile_spread = spawnArgs.GetFloat( "projectile_spread", "0" );
|
||||
num_projectiles = spawnArgs.GetInt( "num_projectiles", "1" );
|
||||
|
||||
GetMuzzle( jointname, muzzle, axis );
|
||||
|
||||
if ( !projectile.GetEntity() ) {
|
||||
CreateProjectile( muzzle, axis[ 0 ] );
|
||||
}
|
||||
|
||||
lastProjectile = projectile.GetEntity();
|
||||
|
||||
tmp = target - muzzle;
|
||||
tmp.Normalize();
|
||||
axis = tmp.ToMat3();
|
||||
|
||||
// rotate it because the cone points up by default
|
||||
tmp = axis[2];
|
||||
axis[2] = axis[0];
|
||||
axis[0] = -tmp;
|
||||
|
||||
// make sure the projectile starts inside the monster bounding box
|
||||
const idBounds &ownerBounds = physicsObj.GetAbsBounds();
|
||||
projClip = lastProjectile->GetPhysics()->GetClipModel();
|
||||
projBounds = projClip->GetBounds().Rotate( axis );
|
||||
|
||||
// check if the owner bounds is bigger than the projectile bounds
|
||||
if ( ( ( ownerBounds[1][0] - ownerBounds[0][0] ) > ( projBounds[1][0] - projBounds[0][0] ) ) &&
|
||||
( ( ownerBounds[1][1] - ownerBounds[0][1] ) > ( projBounds[1][1] - projBounds[0][1] ) ) &&
|
||||
( ( ownerBounds[1][2] - ownerBounds[0][2] ) > ( projBounds[1][2] - projBounds[0][2] ) ) ) {
|
||||
if ( (ownerBounds - projBounds).RayIntersection( muzzle, viewAxis[ 0 ], distance ) ) {
|
||||
start = muzzle + distance * viewAxis[ 0 ];
|
||||
} else {
|
||||
start = ownerBounds.GetCenter();
|
||||
}
|
||||
} else {
|
||||
// projectile bounds bigger than the owner bounds, so just start it from the center
|
||||
start = ownerBounds.GetCenter();
|
||||
}
|
||||
|
||||
gameLocal.clip.Translation( tr, start, muzzle, projClip, axis, MASK_SHOT_RENDERMODEL, this );
|
||||
muzzle = tr.endpos;
|
||||
|
||||
// set aiming direction
|
||||
//GetAimDir( muzzle, target, this, dir );
|
||||
dir = target - muzzle;
|
||||
ang = dir.ToAngles();
|
||||
|
||||
// adjust his aim so it's not perfect. uses sine based movement so the tracers appear less random in their spread.
|
||||
float t = MS2SEC( gameLocal.time + entityNumber * 497 );
|
||||
ang.pitch += idMath::Sin16( t * 5.1 ) * attack_accuracy;
|
||||
ang.yaw += idMath::Sin16( t * 6.7 ) * attack_accuracy;
|
||||
|
||||
if ( clampToAttackCone ) {
|
||||
// clamp the attack direction to be within monster's attack cone so he doesn't do
|
||||
// things like throw the missile backwards if you're behind him
|
||||
diff = idMath::AngleDelta( ang.yaw, current_yaw );
|
||||
if ( diff > attack_cone ) {
|
||||
ang.yaw = current_yaw + attack_cone;
|
||||
} else if ( diff < -attack_cone ) {
|
||||
ang.yaw = current_yaw - attack_cone;
|
||||
}
|
||||
}
|
||||
|
||||
axis = ang.ToMat3();
|
||||
|
||||
float spreadRad = DEG2RAD( projectile_spread );
|
||||
for( i = 0; i < num_projectiles; i++ ) {
|
||||
// spread the projectiles out
|
||||
angle = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() );
|
||||
spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat();
|
||||
dir = axis[ 0 ] + axis[ 2 ] * ( angle * idMath::Sin( spin ) ) - axis[ 1 ] * ( angle * idMath::Cos( spin ) );
|
||||
dir.Normalize();
|
||||
|
||||
// launch the projectile
|
||||
if ( !projectile.GetEntity() ) {
|
||||
CreateProjectile( muzzle, dir );
|
||||
}
|
||||
lastProjectile = projectile.GetEntity();
|
||||
lastProjectile->Launch( muzzle, dir, vec3_origin );
|
||||
projectile = NULL;
|
||||
}
|
||||
|
||||
TriggerWeaponEffects( muzzle, axis );
|
||||
|
||||
lastAttackTime = gameLocal.time;
|
||||
|
||||
//HUMANHEAD mdc - added to support multiple projectiles
|
||||
projectile = NULL;
|
||||
SetCurrentProjectile( projectileDefaultDefIndex ); //set back to our default projectile to be on the safe side
|
||||
//HUMANHEAD END
|
||||
|
||||
return lastProjectile;
|
||||
}
|
||||
|
||||
//
|
||||
// LaunchProjectile
|
||||
//
|
||||
idProjectile *hhGasbagSimple::LaunchProjectileAtVec( const idVec3 &startOrigin, const idMat3 &startAxis, const idVec3 &target, bool clampToAttackCone, const idDict* desiredProjectileDef ) { //HUMANHEAD mdc - added desiredProjectileDef for supporting multiple projs.
|
||||
idVec3 muzzle;
|
||||
idVec3 dir;
|
||||
idVec3 start;
|
||||
trace_t tr;
|
||||
idBounds projBounds;
|
||||
float distance;
|
||||
const idClipModel *projClip;
|
||||
//float attack_accuracy;
|
||||
//float attack_cone;
|
||||
float projectile_spread;
|
||||
float angle;
|
||||
float spin;
|
||||
idAngles ang;
|
||||
int num_projectiles;
|
||||
int i;
|
||||
idMat3 axis;
|
||||
idVec3 tmp;
|
||||
idProjectile *lastProjectile;
|
||||
|
||||
//HUMANHEAD mdc - added to support multiple projectiles
|
||||
if( desiredProjectileDef ) { //try to set our projectile to the desiredProjectile
|
||||
int projIndex = FindProjectileInfo( desiredProjectileDef );
|
||||
if( projIndex >= 0 ) {
|
||||
SetCurrentProjectile( projIndex );
|
||||
}
|
||||
}
|
||||
//HUMANHEAD END
|
||||
|
||||
|
||||
if ( !projectileDef ) {
|
||||
gameLocal.Warning( "%s (%s) doesn't have a projectile specified", name.c_str(), GetEntityDefName() );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//attack_accuracy = spawnArgs.GetFloat( "attack_accuracy", "7" );
|
||||
//attack_cone = spawnArgs.GetFloat( "attack_cone", "70" );
|
||||
projectile_spread = spawnArgs.GetFloat( "projectile_spread", "0" );
|
||||
num_projectiles = spawnArgs.GetInt( "num_projectiles", "1" );
|
||||
|
||||
muzzle = startOrigin;
|
||||
axis = startAxis;
|
||||
|
||||
if ( !projectile.GetEntity() ) {
|
||||
CreateProjectile( muzzle, axis[ 0 ] );
|
||||
}
|
||||
|
||||
lastProjectile = projectile.GetEntity();
|
||||
|
||||
// make sure the projectile starts inside the monster bounding box
|
||||
const idBounds &ownerBounds = physicsObj.GetAbsBounds();
|
||||
projClip = lastProjectile->GetPhysics()->GetClipModel();
|
||||
projBounds = projClip->GetBounds().Rotate( axis );
|
||||
|
||||
// check if the owner bounds is bigger than the projectile bounds
|
||||
if ( ( ( ownerBounds[1][0] - ownerBounds[0][0] ) > ( projBounds[1][0] - projBounds[0][0] ) ) &&
|
||||
( ( ownerBounds[1][1] - ownerBounds[0][1] ) > ( projBounds[1][1] - projBounds[0][1] ) ) &&
|
||||
( ( ownerBounds[1][2] - ownerBounds[0][2] ) > ( projBounds[1][2] - projBounds[0][2] ) ) ) {
|
||||
if ( (ownerBounds - projBounds).RayIntersection( muzzle, viewAxis[ 0 ], distance ) ) {
|
||||
start = muzzle + distance * viewAxis[ 0 ];
|
||||
} else {
|
||||
start = ownerBounds.GetCenter();
|
||||
}
|
||||
} else {
|
||||
// projectile bounds bigger than the owner bounds, so just start it from the center
|
||||
start = ownerBounds.GetCenter();
|
||||
}
|
||||
|
||||
gameLocal.clip.Translation( tr, start, muzzle, projClip, axis, MASK_SHOT_RENDERMODEL, this );
|
||||
muzzle = tr.endpos;
|
||||
|
||||
float spreadRad = DEG2RAD( projectile_spread );
|
||||
for( i = 0; i < num_projectiles; i++ ) {
|
||||
// spread the projectiles out
|
||||
angle = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() );
|
||||
spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat();
|
||||
dir = axis[ 0 ] + axis[ 2 ] * ( angle * idMath::Sin( spin ) ) - axis[ 1 ] * ( angle * idMath::Cos( spin ) );
|
||||
dir.Normalize();
|
||||
|
||||
// launch the projectile
|
||||
if ( !projectile.GetEntity() ) {
|
||||
CreateProjectile( muzzle, dir );
|
||||
}
|
||||
lastProjectile = projectile.GetEntity();
|
||||
lastProjectile->Launch( muzzle, dir, vec3_origin );
|
||||
projectile = NULL;
|
||||
}
|
||||
|
||||
TriggerWeaponEffects( muzzle, axis );
|
||||
|
||||
lastAttackTime = gameLocal.time;
|
||||
|
||||
//HUMANHEAD mdc - added to support multiple projectiles
|
||||
projectile = NULL;
|
||||
SetCurrentProjectile( projectileDefaultDefIndex ); //set back to our default projectile to be on the safe side
|
||||
//HUMANHEAD END
|
||||
|
||||
return lastProjectile;
|
||||
}
|
||||
|
||||
#define LinkScriptVariable( name ) name.LinkTo( scriptObject, #name )
|
||||
void hhGasbagSimple::LinkScriptVariables(void) {
|
||||
hhMonsterAI::LinkScriptVariables();
|
||||
|
||||
LinkScriptVariable(AI_PODCOUNT);
|
||||
LinkScriptVariable(AI_CHARGEDONE);
|
||||
LinkScriptVariable(AI_SWOOP);
|
||||
LinkScriptVariable(AI_DODGEDAMAGE);
|
||||
}
|
||||
|
||||
//
|
||||
// Event_GrabEnemy()
|
||||
//
|
||||
void hhGasbagSimple::Event_GrabEnemy(void) {
|
||||
idThread::ReturnFloat( GrabEnemy() );
|
||||
}
|
||||
|
||||
//
|
||||
// GrabEnemy()
|
||||
//
|
||||
bool hhGasbagSimple::GrabEnemy(void) {
|
||||
if (!enemy.IsValid() || enemy->IsBound() || bindController->GetRider()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't grab Talon
|
||||
if (enemy->IsType(hhTalon::Type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (enemy->IsType(hhPlayer::Type)) {
|
||||
hhPlayer *player = reinterpret_cast<hhPlayer *> (enemy.GetEntity());
|
||||
if (player->IsSpiritOrDeathwalking() || player->InGravityZone()) { // Refuse to grab spirit players, or players affected by a gravity zone
|
||||
return false;
|
||||
}
|
||||
player->Freeze();
|
||||
}
|
||||
|
||||
bindController->Attach(enemy.GetEntity());
|
||||
enemy->SetOrigin(vec3_origin);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Event_BiteEnemy()
|
||||
//
|
||||
void hhGasbagSimple::Event_BiteEnemy(void) {
|
||||
if (!enemy.IsValid() || bindController->GetRider() != enemy.GetEntity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (enemy->IsType(hhSpiritProxy::Type)) {
|
||||
float diff = gameLocal.GetTime() - reinterpret_cast<hhSpiritProxy *> (enemy.GetEntity())->GetActivationTime();
|
||||
if (diff < 1150.0f) {
|
||||
// Don't allow the spirit proxy to ignore damage
|
||||
scriptThread->WaitMS(diff + 250.0f);
|
||||
CancelEvents(&EV_BiteEnemy);
|
||||
PostEventMS(&EV_BiteEnemy, diff + 100.0f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Direct damage
|
||||
bindController->Detach();
|
||||
enemy->Damage(this, this, vec3_origin, spawnArgs.GetString("def_damage_bite"), 1.0f, INVALID_JOINT);
|
||||
}
|
||||
|
||||
//
|
||||
// Damage()
|
||||
//
|
||||
void hhGasbagSimple::Damage(idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location) {
|
||||
int curHealth = health;
|
||||
idEntity *enemyEnt = enemy.GetEntity();
|
||||
hhMonsterAI::Damage(inflictor, attacker, dir, damageDefName, damageScale, location);
|
||||
|
||||
if (health < curHealth) {
|
||||
if (gameLocal.time > nextWoundTime && location != INVALID_JOINT) {
|
||||
hhFxInfo fx;
|
||||
fx.SetEntity(this);
|
||||
fx.RemoveWhenDone(true);
|
||||
SpawnFxLocal(spawnArgs.GetString("fx_damage"), inflictor->GetOrigin(), dir.ToMat3(), &fx);
|
||||
|
||||
nextWoundTime = gameLocal.time + 250;
|
||||
}
|
||||
|
||||
// Only release enemy if we actually took damage
|
||||
idEntity *rider;
|
||||
if ((rider = bindController->GetRider()) != NULL) {
|
||||
bindController->Detach();
|
||||
if (rider->IsType(hhPlayer::Type)) {
|
||||
rider->PostEventMS(&EV_Unfreeze, 0);
|
||||
}
|
||||
}
|
||||
|
||||
AI_DODGEDAMAGE = AI_DODGEDAMAGE + (curHealth - health);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Event_DirectMoveToPosition()
|
||||
//
|
||||
void hhGasbagSimple::Event_DirectMoveToPosition(const idVec3 &pos) {
|
||||
StopMove(MOVE_STATUS_DONE);
|
||||
DirectMoveToPosition(pos);
|
||||
}
|
||||
|
||||
//
|
||||
// Event_ChargeEnemy
|
||||
//
|
||||
void hhGasbagSimple::Event_ChargeEnemy(void) {
|
||||
AI_CHARGEDONE = true;
|
||||
StopMove(MOVE_STATUS_DEST_NOT_FOUND);
|
||||
if (!enemy.IsValid() || enemy->IsBound()) {
|
||||
return;
|
||||
}
|
||||
|
||||
idVec3 enemyOrg;
|
||||
|
||||
fly_offset = 0.0f;
|
||||
|
||||
// position destination so that we're in the enemy's view
|
||||
enemyOrg = enemy->GetEyePosition();
|
||||
enemyOrg -= enemy->GetPhysics()->GetGravityNormal() * fly_offset;
|
||||
|
||||
DirectMoveToPosition(enemyOrg);
|
||||
HH_ASSERT(move.bEnemyBlocks == false);
|
||||
}
|
||||
|
||||
void hhGasbagSimple::Event_EnemyIsSpirit( hhPlayer *player, hhSpiritProxy *proxy ) {
|
||||
hhMonsterAI::Event_EnemyIsSpirit(player, proxy);
|
||||
if (bindController->GetRider() == player) {
|
||||
bindController->Detach();
|
||||
player->Unfreeze();
|
||||
GrabEnemy();
|
||||
}
|
||||
}
|
||||
|
||||
void hhGasbagSimple::Event_EnemyIsPhysical( hhPlayer *player, hhSpiritProxy *proxy ) {
|
||||
hhMonsterAI::Event_EnemyIsPhysical(player, proxy);
|
||||
if (bindController->GetRider() == player) {
|
||||
player->Freeze();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Event_BindUnfroze
|
||||
//
|
||||
void hhGasbagSimple::Event_BindUnfroze(idEntity *unfrozenBind) {
|
||||
if (!enemy.IsValid() || unfrozenBind != enemy.GetEntity() || bindController->GetRider() != enemy.GetEntity()) {
|
||||
gameLocal.Warning("Gasbag received BindUnfroze event but enemy is not bound to it.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
bindController->Detach();
|
||||
}
|
||||
|
||||
//
|
||||
// FlyTurn
|
||||
//
|
||||
void hhGasbagSimple::FlyTurn(void) {
|
||||
if (!AI_SWOOP && (AI_ENEMY_VISIBLE || move.moveCommand == MOVE_FACE_ENEMY)) {
|
||||
TurnToward( lastVisibleEnemyPos );
|
||||
} else if ((move.moveCommand == MOVE_FACE_ENTITY) && move.goalEntity.GetEntity()) {
|
||||
TurnToward(move.goalEntity.GetEntity()->GetPhysics()->GetOrigin());
|
||||
} else if (move.speed > 0.1f) {
|
||||
const idVec3 &vel = physicsObj.GetLinearVelocity();
|
||||
if (vel.ToVec2().LengthSqr() > 0.1f) {
|
||||
TurnToward(vel.ToYaw());
|
||||
}
|
||||
}
|
||||
Turn();
|
||||
}
|
||||
|
||||
//
|
||||
// Event_GrabCheck()
|
||||
//
|
||||
void hhGasbagSimple::Event_GrabCheck(void) {
|
||||
idThread::ReturnInt(enemy.IsValid() && enemy->IsBoundTo(this));
|
||||
}
|
||||
|
||||
|
||||
void hhGasbagSimple::Event_MoveToGrabPosition(void) {
|
||||
StopMove(MOVE_STATUS_DEST_NOT_FOUND);
|
||||
if (!enemy.IsValid() || enemy->IsBound()) {
|
||||
return;
|
||||
}
|
||||
|
||||
fly_offset = 0.0f;
|
||||
|
||||
// position destination so that we're right on the enemy when we grab him
|
||||
DirectMoveToPosition(enemy->GetOrigin() - spawnArgs.GetVector( "grab_offset" ));
|
||||
HH_ASSERT(move.bEnemyBlocks == false);
|
||||
}
|
||||
|
||||
void hhGasbagSimple::SetEnemy(idActor *newEnemy) {
|
||||
idEntity *rider;
|
||||
if ((rider = bindController->GetRider())) { // This is usually caused by Talon
|
||||
if (rider->IsType(hhPlayer::Type)) {
|
||||
rider->PostEventMS(&EV_Unfreeze, 0);
|
||||
}
|
||||
bindController->Detach();
|
||||
}
|
||||
|
||||
hhMonsterAI::SetEnemy(newEnemy);
|
||||
}
|
||||
|
||||
void hhGasbagSimple::Event_CheckRange(void) {
|
||||
if (!enemy.IsValid()) {
|
||||
idThread::ReturnInt(0);
|
||||
return;
|
||||
}
|
||||
|
||||
int retval;
|
||||
float dist = ( enemy->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin() ).Length();
|
||||
|
||||
if (enemy->IsType(hhPlayer::Type)) {
|
||||
float min = spawnArgs.GetFloat( "dda_range_min" );
|
||||
float max = spawnArgs.GetFloat( "dda_range_max" );
|
||||
float player_dist = min + (max - min) * gameLocal.GetDDAValue();
|
||||
|
||||
retval = (int) (dist <= player_dist);
|
||||
} else {
|
||||
retval = (int) (dist <= 100.0f);
|
||||
}
|
||||
|
||||
idThread::ReturnInt(retval);
|
||||
}
|
||||
|
||||
void hhGasbagSimple::Gib( const idVec3 &dir, const char *damageDefName ) {
|
||||
// Bypass idActor::Gib()
|
||||
idAFEntity_Gibbable::Gib( dir, damageDefName );
|
||||
}
|
||||
|
||||
int hhGasbagSimple::ReactionTo( const idEntity *ent ) {
|
||||
//gasbags skip the spirit stuff in hhMonsterAI::ReactionTo()
|
||||
if ( bNoCombat ) {
|
||||
return ATTACK_IGNORE;
|
||||
}
|
||||
const idActor *actor = static_cast<const idActor *>( ent );
|
||||
if( actor && actor->IsType(hhDeathProxy::Type) ) {
|
||||
return ATTACK_IGNORE;
|
||||
}
|
||||
if ( ent->IsType( hhMonsterAI::Type ) ) {
|
||||
const hhMonsterAI *entAI = static_cast<const hhMonsterAI *>( ent );
|
||||
if ( entAI && entAI->bNeverTarget ) {
|
||||
return ATTACK_IGNORE;
|
||||
}
|
||||
}
|
||||
|
||||
return idAI::ReactionTo( ent );
|
||||
}
|
||||
|
||||
void hhGasbagSimple::Event_EnemyRangeZ( void ) {
|
||||
idThread::ReturnFloat( lastVisibleEnemyPos.z - GetOrigin().z );
|
||||
}
|
||||
#endif //HUMANHEAD jsh PCF 5/26/06: code removed for demo build
|
||||
Reference in New Issue
Block a user