0b792a6355
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@3053 fc73d0e0-1445-4013-8a0c-d673dee63da5
366 lines
No EOL
6.4 KiB
C++
366 lines
No EOL
6.4 KiB
C++
|
||
/*
|
||
|
||
A monster is in fight mode if it thinks it can effectively attack its
|
||
enemy.
|
||
|
||
When it decides it can't attack, it goes into hunt mode.
|
||
|
||
*/
|
||
|
||
float(float v) anglemod;
|
||
|
||
void() ai_face;
|
||
|
||
//=============================================================================
|
||
|
||
/*
|
||
===========
|
||
CheckAttack
|
||
|
||
The player is in view, so decide to move or launch an attack
|
||
Returns FALSE if movement should continue
|
||
============
|
||
*/
|
||
BOOL(float enemy_range) CheckAttack =
|
||
{
|
||
local vector spot1, spot2;
|
||
local entity targ;
|
||
local float chance;
|
||
|
||
targ = self.enemy;
|
||
|
||
// see if any entities are in the way of the shot
|
||
spot1 = self.origin + self.view_ofs;
|
||
spot2 = targ.origin + targ.view_ofs;
|
||
|
||
traceline (spot1, spot2, FALSE, self);
|
||
|
||
if (trace_ent != targ)
|
||
return FALSE; // don't have a clear shot
|
||
|
||
if (trace_inopen && trace_inwater)
|
||
return FALSE; // sight line crossed contents
|
||
|
||
if (enemy_range == RANGE_MELEE)
|
||
{ // melee attack
|
||
if (self.th_melee)
|
||
{
|
||
self.th_melee ();
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
// missile attack
|
||
if (!self.th_missile)
|
||
return FALSE;
|
||
|
||
if (time < self.attack_finished)
|
||
return FALSE;
|
||
|
||
if (enemy_range == RANGE_FAR)
|
||
return FALSE;
|
||
|
||
if (enemy_range == RANGE_MELEE)
|
||
{
|
||
chance = 0.9;
|
||
self.attack_finished = 0;
|
||
}
|
||
else if (enemy_range == RANGE_NEAR)
|
||
{
|
||
if (self.th_melee)
|
||
chance = 0.2;
|
||
else
|
||
chance = 0.4;
|
||
}
|
||
else if (enemy_range == RANGE_MID)
|
||
{
|
||
if (self.th_melee)
|
||
chance = 0.05;
|
||
else
|
||
chance = 0.1;
|
||
}
|
||
else
|
||
chance = 0;
|
||
|
||
if (random () < chance)
|
||
{
|
||
self.th_missile ();
|
||
SUB_AttackFinished (2*random());
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
};
|
||
|
||
|
||
/*
|
||
=============
|
||
ai_face
|
||
|
||
Stay facing the enemy
|
||
=============
|
||
*/
|
||
void() ai_face =
|
||
{
|
||
self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);
|
||
ChangeYaw ();
|
||
};
|
||
|
||
/*
|
||
=============
|
||
ai_charge
|
||
|
||
The monster is in a melee attack, so get as close as possible to .enemy
|
||
=============
|
||
*/
|
||
BOOL (entity targ) visible;
|
||
BOOL(entity targ) infront;
|
||
float(entity targ) range;
|
||
|
||
void(float d) ai_charge =
|
||
{
|
||
ai_face ();
|
||
movetogoal (d); // done in C code...
|
||
};
|
||
|
||
void() ai_charge_side =
|
||
{
|
||
local vector dtemp;
|
||
local float heading;
|
||
|
||
// aim to the left of the enemy for a flyby
|
||
|
||
self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);
|
||
ChangeYaw ();
|
||
|
||
makevectors (self.angles);
|
||
dtemp = self.enemy.origin - 30*v_right;
|
||
heading = vectoyaw(dtemp - self.origin);
|
||
|
||
walkmove(heading, 20);
|
||
};
|
||
|
||
|
||
/*
|
||
=============
|
||
ai_melee
|
||
|
||
=============
|
||
*/
|
||
void(INTEGER mod) ai_melee =
|
||
{
|
||
local vector delta;
|
||
local float ldmg;
|
||
|
||
if (!self.enemy)
|
||
return; // removed before stroke
|
||
|
||
delta = self.enemy.origin - self.origin;
|
||
|
||
if (vlen(delta) > 60)
|
||
return;
|
||
|
||
ldmg = (random() + random() + random()) * 3;
|
||
T_Damage (self.enemy, self, self, ldmg, mod);
|
||
};
|
||
|
||
|
||
void(INTEGER mod) ai_melee_side =
|
||
{
|
||
local vector delta;
|
||
local float ldmg;
|
||
|
||
if (!self.enemy)
|
||
return; // removed before stroke
|
||
|
||
ai_charge_side();
|
||
|
||
delta = self.enemy.origin - self.origin;
|
||
|
||
if (vlen(delta) > 60)
|
||
return;
|
||
if (!CanDamage (self.enemy, self))
|
||
return;
|
||
ldmg = (random() + random() + random()) * 3;
|
||
T_Damage (self.enemy, self, self, ldmg, mod);
|
||
};
|
||
|
||
|
||
//=============================================================================
|
||
|
||
/*
|
||
===========
|
||
SoldierCheckAttack
|
||
|
||
The player is in view, so decide to move or launch an attack
|
||
Returns FALSE if movement should continue
|
||
============
|
||
*/
|
||
BOOL(float enemy_range) SoldierCheckAttack =
|
||
{
|
||
local vector spot1, spot2;
|
||
local entity targ;
|
||
local float chance;
|
||
|
||
targ = self.enemy;
|
||
|
||
// see if any entities are in the way of the shot
|
||
spot1 = self.origin + self.view_ofs;
|
||
spot2 = targ.origin + targ.view_ofs;
|
||
|
||
traceline (spot1, spot2, FALSE, self);
|
||
|
||
if (trace_inopen && trace_inwater)
|
||
return FALSE; // sight line crossed contents
|
||
|
||
if (trace_ent != targ)
|
||
return FALSE; // don't have a clear shot
|
||
|
||
|
||
// missile attack
|
||
if (time < self.attack_finished)
|
||
return FALSE;
|
||
|
||
if (enemy_range == RANGE_FAR)
|
||
return FALSE;
|
||
|
||
if (enemy_range == RANGE_MELEE)
|
||
chance = 0.9;
|
||
else if (enemy_range == RANGE_NEAR)
|
||
chance = 0.4;
|
||
else if (enemy_range == RANGE_MID)
|
||
chance = 0.05;
|
||
else
|
||
chance = 0;
|
||
|
||
if (random() < chance)
|
||
{
|
||
self.th_missile ();
|
||
SUB_AttackFinished (1 + random());
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
};
|
||
//=============================================================================
|
||
|
||
/*
|
||
===========
|
||
ShamCheckAttack
|
||
|
||
The player is in view, so decide to move or launch an attack
|
||
Returns FALSE if movement should continue
|
||
============
|
||
*/
|
||
BOOL(float enemy_range) ShamCheckAttack =
|
||
{
|
||
local vector spot1, spot2;
|
||
local entity targ;
|
||
// local float chance;
|
||
// local float enemy_yaw;
|
||
|
||
if (enemy_range == RANGE_MELEE)
|
||
{
|
||
if (CanDamage (self.enemy, self))
|
||
{
|
||
self.attack_state = AS_MELEE;
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
if (time < self.attack_finished)
|
||
return FALSE;
|
||
|
||
targ = self.enemy;
|
||
|
||
// see if any entities are in the way of the shot
|
||
spot1 = self.origin + self.view_ofs;
|
||
spot2 = targ.origin + targ.view_ofs;
|
||
|
||
if (vlen(spot1 - spot2) > 600)
|
||
return FALSE;
|
||
|
||
traceline (spot1, spot2, FALSE, self);
|
||
|
||
if (trace_inopen && trace_inwater)
|
||
return FALSE; // sight line crossed contents
|
||
|
||
if (trace_ent != targ)
|
||
{
|
||
return FALSE; // don't have a clear shot
|
||
}
|
||
|
||
// missile attack
|
||
if (enemy_range == RANGE_FAR)
|
||
return FALSE;
|
||
|
||
self.attack_state = AS_MISSILE;
|
||
SUB_AttackFinished (2 + 2*random());
|
||
return TRUE;
|
||
};
|
||
|
||
//============================================================================
|
||
|
||
/*
|
||
===========
|
||
OgreCheckAttack
|
||
|
||
The player is in view, so decide to move or launch an attack
|
||
Returns FALSE if movement should continue
|
||
============
|
||
*/
|
||
BOOL(float enemy_range) OgreCheckAttack =
|
||
{
|
||
local vector spot1, spot2;
|
||
local entity targ;
|
||
local float chance;
|
||
|
||
if (enemy_range == RANGE_MELEE)
|
||
{
|
||
if (CanDamage (self.enemy, self))
|
||
{
|
||
self.attack_state = AS_MELEE;
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
if (time < self.attack_finished)
|
||
return FALSE;
|
||
|
||
targ = self.enemy;
|
||
|
||
// see if any entities are in the way of the shot
|
||
spot1 = self.origin + self.view_ofs;
|
||
spot2 = targ.origin + targ.view_ofs;
|
||
|
||
traceline (spot1, spot2, FALSE, self);
|
||
|
||
if (trace_inopen && trace_inwater)
|
||
return FALSE; // sight line crossed contents
|
||
|
||
if (trace_ent != targ)
|
||
{
|
||
return FALSE; // don't have a clear shot
|
||
}
|
||
|
||
// missile attack
|
||
if (time < self.attack_finished)
|
||
return FALSE;
|
||
|
||
if (enemy_range == RANGE_FAR)
|
||
return FALSE;
|
||
|
||
else if (enemy_range == RANGE_NEAR)
|
||
chance = 0.10;
|
||
else if (enemy_range == RANGE_MID)
|
||
chance = 0.05;
|
||
else
|
||
chance = 0;
|
||
|
||
self.attack_state = AS_MISSILE;
|
||
SUB_AttackFinished (1 + 2*random());
|
||
return TRUE;
|
||
};
|
||
|
||
|