2016-03-27 11:49:47 +02:00
|
|
|
/*
|
|
|
|
===========================================================================
|
2023-10-12 18:19:22 +02:00
|
|
|
Copyright (C) 2023 the OpenMoHAA team
|
2016-03-27 11:49:47 +02:00
|
|
|
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
|
|
|
|
OpenMoHAA source code is free software; you can redistribute it
|
|
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
|
|
or (at your option) any later version.
|
|
|
|
|
|
|
|
OpenMoHAA source code is distributed in the hope that it will be
|
|
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with OpenMoHAA source code; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
// actor_grenade.cpp
|
|
|
|
|
|
|
|
#include "actor.h"
|
2018-09-05 16:55:10 +02:00
|
|
|
#include "weaputils.h"
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
bool Actor::Grenade_Acquire(eGrenadeState eNextState, const_str csReturnAnim)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-22 15:41:19 +02:00
|
|
|
vec3_t vDest;
|
2023-10-12 18:19:22 +02:00
|
|
|
bool bRetVal = false;
|
2023-10-22 15:41:19 +02:00
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
if (m_bGrenadeBounced) {
|
2023-10-22 15:41:19 +02:00
|
|
|
m_bGrenadeBounced = false;
|
|
|
|
bRetVal = true;
|
2023-10-12 18:19:22 +02:00
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
VectorSubtract(origin, m_vGrenadePos, vDest);
|
|
|
|
VectorNormalizeFast(vDest);
|
2023-10-12 18:19:22 +02:00
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
VectorMA(m_vGrenadePos, 16, vDest, vDest);
|
2023-10-12 18:19:22 +02:00
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
SetPath(vDest, NULL, 0, NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!PathExists()) {
|
2023-10-12 18:19:22 +02:00
|
|
|
m_bGrenadeBounced = true;
|
|
|
|
m_eGrenadeState = AI_GRENSTATE_FLEE;
|
|
|
|
Grenade_Flee();
|
2023-10-22 15:41:19 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PathComplete()) {
|
|
|
|
ForwardLook();
|
|
|
|
|
|
|
|
if (VectorLength2DSquared(m_pGrenade->velocity) >= Square(32)) {
|
|
|
|
Anim_Stand();
|
|
|
|
} else {
|
|
|
|
m_pGrenade->velocity = vec_zero;
|
|
|
|
m_eGrenadeState = eNextState;
|
|
|
|
DesiredAnimation(ANIM_MODE_NORMAL, csReturnAnim);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Anim_RunToCasual(ANIM_MODE_PATH_GOAL);
|
|
|
|
VectorSub2D(origin, m_vGrenadePos, vDest);
|
|
|
|
|
|
|
|
if (VectorLength2DSquared(vDest) > Square(32)) {
|
|
|
|
FaceMotion();
|
|
|
|
} else {
|
|
|
|
SetDesiredYawDest(m_vGrenadePos);
|
|
|
|
}
|
2023-10-12 18:19:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return bRetVal;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-10-12 20:17:09 +02:00
|
|
|
void Actor::InitGrenade(GlobalFuncs_t *func)
|
|
|
|
{
|
|
|
|
func->ThinkState = &Actor::Think_Grenade;
|
|
|
|
func->BeginState = &Actor::Begin_Grenade;
|
|
|
|
func->EndState = &Actor::End_Grenade;
|
|
|
|
func->ResumeState = &Actor::Resume_Grenade;
|
|
|
|
func->SuspendState = &Actor::End_Grenade;
|
|
|
|
func->PassesTransitionConditions = &Actor::PassesTransitionConditions_Grenade;
|
|
|
|
func->FinishedAnimation = &Actor::FinishedAnimation_Grenade;
|
|
|
|
func->IsState = &Actor::IsGrenadeState;
|
|
|
|
}
|
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
void Actor::Grenade_Flee(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-22 15:41:19 +02:00
|
|
|
vec2_t vDirPreferred;
|
|
|
|
vec2_t vDirAway;
|
|
|
|
float fAngle, fSinAngle, fCosAngle;
|
|
|
|
float fMinCloseDistSquared;
|
|
|
|
vec2_t grenade_offset;
|
|
|
|
float origin_ratio;
|
|
|
|
PathInfo *current_node;
|
2023-10-12 18:19:22 +02:00
|
|
|
|
|
|
|
if (m_bGrenadeBounced) {
|
2023-10-22 15:41:19 +02:00
|
|
|
fAngle = (rand() - 0x3FFFFFFF) / 683565275.f;
|
2023-10-12 18:19:22 +02:00
|
|
|
fSinAngle = sin(fAngle);
|
|
|
|
fCosAngle = cos(fAngle);
|
|
|
|
|
|
|
|
VectorSub2D(origin, m_vGrenadePos, vDirAway);
|
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
vDirPreferred[0] = vDirAway[0] * fCosAngle - vDirAway[1] * fSinAngle;
|
2023-10-12 18:19:22 +02:00
|
|
|
vDirPreferred[1] = vDirAway[0] * fSinAngle + vDirAway[1] * fCosAngle;
|
|
|
|
|
|
|
|
FindPathAway(m_vGrenadePos, vDirPreferred, 512);
|
|
|
|
|
|
|
|
if (PathExists() && !PathComplete()) {
|
2023-10-22 15:41:19 +02:00
|
|
|
fMinCloseDistSquared = VectorLength2DSquared(vDirAway) * 0.64f;
|
2023-10-12 18:19:22 +02:00
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
if (fMinCloseDistSquared < Square(64)) {
|
2023-10-12 18:19:22 +02:00
|
|
|
fMinCloseDistSquared = 0;
|
|
|
|
}
|
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
for (current_node = CurrentPathNode(); current_node >= LastPathNode(); current_node--) {
|
2023-10-12 18:19:22 +02:00
|
|
|
VectorSub2D(m_vGrenadePos, current_node->point, grenade_offset);
|
2023-10-22 15:41:19 +02:00
|
|
|
|
|
|
|
origin_ratio = DotProduct2D(grenade_offset, current_node->dir);
|
|
|
|
if (origin_ratio <= 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (origin_ratio > current_node->dist) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Square(CrossProduct2D(grenade_offset, current_node->dir)) < fMinCloseDistSquared) {
|
|
|
|
ClearPath();
|
|
|
|
break;
|
2023-10-12 18:19:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-10-22 15:41:19 +02:00
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
m_bGrenadeBounced = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PathExists() && !PathComplete()) {
|
|
|
|
Sentient *pOwner = NULL;
|
2023-10-22 15:41:19 +02:00
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
if (m_pGrenade && m_pGrenade->IsSubclassOfProjectile()) {
|
2023-10-22 15:41:19 +02:00
|
|
|
pOwner = static_cast<Projectile *>(m_pGrenade.Pointer())->GetOwner();
|
2023-10-12 18:19:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pOwner && pOwner->m_Team == m_Team) {
|
2023-10-22 15:41:19 +02:00
|
|
|
Anim_RunTo(ANIM_MODE_PATH_GOAL);
|
2023-10-12 18:19:22 +02:00
|
|
|
} else {
|
2023-10-22 15:41:19 +02:00
|
|
|
Anim_RunToFlee(ANIM_MODE_PATH_GOAL);
|
2023-10-12 18:19:22 +02:00
|
|
|
}
|
2023-10-22 15:41:19 +02:00
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
FaceMotion();
|
2023-10-22 15:41:19 +02:00
|
|
|
}
|
|
|
|
else if ((origin - m_vGrenadePos).lengthXYSquared() >= 100352
|
|
|
|
|| !G_SightTrace(
|
|
|
|
centroid, vec_zero, vec_zero, m_vGrenadePos, this, m_pGrenade, MASK_CANSEE, 0, "Actor::Grenade_Flee"
|
|
|
|
)) {
|
|
|
|
m_eGrenadeState = AI_GRENSTATE_FLEE_SUCCESS;
|
|
|
|
Anim_Attack();
|
|
|
|
AimAtTargetPos();
|
2023-10-12 18:19:22 +02:00
|
|
|
} else {
|
2023-10-22 15:41:19 +02:00
|
|
|
m_eGrenadeState = AI_GRENSTATE_FLEE_FAIL;
|
|
|
|
ForwardLook();
|
|
|
|
Anim_Cower();
|
2023-10-12 18:19:22 +02:00
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
void Actor::Grenade_ThrowAcquire(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-22 15:41:19 +02:00
|
|
|
if (Grenade_Acquire(AI_GRENSTATE_THROW, STRING_ANIM_GRENADERETURN_SCR)) {
|
|
|
|
return;
|
2023-10-12 18:19:22 +02:00
|
|
|
}
|
2023-10-22 15:41:19 +02:00
|
|
|
|
|
|
|
if (CanGetGrenadeFromAToB(m_vGrenadePos, m_vLastEnemyPos, true, &m_vGrenadeVel, &m_eGrenadeMode)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_eGrenadeState = AI_GRENSTATE_FLEE;
|
|
|
|
m_bGrenadeBounced = true;
|
|
|
|
Grenade_Flee();
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
void Actor::Grenade_Throw(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-22 15:41:19 +02:00
|
|
|
ForwardLook();
|
2023-10-12 18:19:22 +02:00
|
|
|
SetDesiredYawDir(m_vGrenadeVel);
|
|
|
|
ContinueAnimation();
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
void Actor::Grenade_KickAcquire(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-22 15:41:19 +02:00
|
|
|
Vector vDelta;
|
2023-10-12 18:19:22 +02:00
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
if (Grenade_Acquire(AI_GRENSTATE_KICK, STRING_ANIM_GRENADEKICK_SCR)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
vDelta = m_vGrenadePos - origin;
|
|
|
|
vDelta.z = 0;
|
|
|
|
|
|
|
|
if (CanKickGrenade(m_vGrenadePos, m_vLastEnemyPos, vDelta, &m_vGrenadeVel)) {
|
|
|
|
m_vKickDir = m_vGrenadeVel;
|
|
|
|
m_vKickDir.z = 0;
|
|
|
|
m_vKickDir.normalizefast();
|
|
|
|
} else {
|
|
|
|
m_eGrenadeState = AI_GRENSTATE_FLEE;
|
|
|
|
m_bGrenadeBounced = true;
|
|
|
|
Grenade_Flee();
|
2023-10-12 18:19:22 +02:00
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
void Actor::Grenade_Kick(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-22 15:41:19 +02:00
|
|
|
ForwardLook();
|
2023-10-12 18:19:22 +02:00
|
|
|
ContinueAnimation();
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
void Actor::Grenade_MartyrAcquire(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-22 15:41:19 +02:00
|
|
|
vec3_t vDest;
|
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
if (m_bGrenadeBounced) {
|
|
|
|
m_bGrenadeBounced = false;
|
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
VectorSubtract(origin, m_vGrenadePos, vDest);
|
2023-10-12 18:19:22 +02:00
|
|
|
VectorNormalizeFast(vDest);
|
2023-10-22 15:41:19 +02:00
|
|
|
VectorMA(m_vGrenadePos, 88, vDest, vDest);
|
2023-10-12 18:19:22 +02:00
|
|
|
|
|
|
|
SetPath(vDest, NULL, 0, NULL, 0.0);
|
|
|
|
}
|
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
if (!PathExists()) {
|
|
|
|
m_bGrenadeBounced = true;
|
|
|
|
m_eGrenadeState = AI_GRENSTATE_FLEE;
|
|
|
|
Grenade_Flee();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PathComplete()) {
|
|
|
|
ForwardLook();
|
|
|
|
m_pGrenade->velocity = vec_zero;
|
2023-10-12 18:19:22 +02:00
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
if (m_pGrenade->velocity.lengthXYSquared() < Square(32)) {
|
2023-10-12 18:19:22 +02:00
|
|
|
m_pGrenade->velocity = vec_zero;
|
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
m_eGrenadeState = AI_GRENSTATE_MARTYR;
|
|
|
|
m_iStateTime = level.inttime;
|
|
|
|
Grenade_Martyr();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Anim_RunToCasual(ANIM_MODE_PATH_GOAL);
|
|
|
|
SetPathGoalEndAnim(STRING_ANIM_GRENADEMARTYR_SCR);
|
2023-10-12 18:19:22 +02:00
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
VectorSub2D(origin, m_vGrenadePos, vDest);
|
|
|
|
if (VectorLength2DSquared(vDest) > Square(128)) {
|
|
|
|
FaceMotion();
|
2023-10-12 18:19:22 +02:00
|
|
|
} else {
|
2023-10-22 15:41:19 +02:00
|
|
|
SetDesiredYawDest(m_vGrenadePos);
|
2023-10-12 18:19:22 +02:00
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
void Actor::Grenade_Martyr(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-12 18:19:22 +02:00
|
|
|
if (m_pGrenade && level.inttime >= (m_iStateTime + 1000) - 0.5) {
|
2023-10-22 15:41:19 +02:00
|
|
|
static_cast<Projectile *>(m_pGrenade.Pointer())->SetMartyr(entnum);
|
2023-10-12 18:19:22 +02:00
|
|
|
}
|
2018-09-05 16:55:10 +02:00
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
ForwardLook();
|
2023-10-12 18:19:22 +02:00
|
|
|
ContinueAnimation();
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
void Actor::Grenade_Wait(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-12 18:19:22 +02:00
|
|
|
if (rand() & 0xF) {
|
|
|
|
Anim_Cower();
|
|
|
|
} else {
|
|
|
|
Anim_Stand();
|
|
|
|
Grenade_NextThinkState();
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
void Actor::Grenade_NextThinkState(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-22 15:41:19 +02:00
|
|
|
if (!m_Enemy || (m_Enemy->flags & FL_NOTARGET)) {
|
2023-10-15 20:52:06 +02:00
|
|
|
SetThinkState(THINKSTATE_IDLE, THINKLEVEL_IDLE);
|
2023-10-22 15:41:19 +02:00
|
|
|
return;
|
2023-10-12 18:19:22 +02:00
|
|
|
}
|
2023-10-22 15:41:19 +02:00
|
|
|
|
|
|
|
SetThinkState(THINKSTATE_ATTACK, THINKLEVEL_IDLE);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
void Actor::Grenade_EventAttach(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-22 15:41:19 +02:00
|
|
|
if (!m_pGrenade) {
|
|
|
|
return;
|
|
|
|
}
|
2023-10-12 18:19:22 +02:00
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
int tagnum;
|
|
|
|
Vector vVel;
|
|
|
|
eGrenadeTossMode eMode;
|
|
|
|
|
|
|
|
tagnum = gi.Tag_NumForName(edict->tiki, "tag_weapon_right");
|
|
|
|
if (tagnum >= 0) {
|
|
|
|
m_pGrenade->attach(entnum, tagnum, qtrue, vec3_origin);
|
|
|
|
}
|
2023-10-12 18:19:22 +02:00
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
m_pGrenade->avelocity = vec3_origin;
|
|
|
|
|
|
|
|
if (CanGetGrenadeFromAToB(origin, m_vLastEnemyPos, true, &vVel, &eMode)) {
|
|
|
|
m_vGrenadeVel = vVel;
|
|
|
|
m_eGrenadeMode = eMode;
|
2023-10-12 18:19:22 +02:00
|
|
|
}
|
2023-10-22 15:41:19 +02:00
|
|
|
|
|
|
|
SetDesiredYawDir(m_vGrenadeVel);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
void Actor::Grenade_EventDetach(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-22 15:41:19 +02:00
|
|
|
if (!m_pGrenade) {
|
|
|
|
return;
|
2023-10-12 18:19:22 +02:00
|
|
|
}
|
2023-10-22 15:41:19 +02:00
|
|
|
|
|
|
|
const_str csAnim;
|
|
|
|
|
|
|
|
m_pGrenade->detach();
|
|
|
|
csAnim = m_eGrenadeMode == AI_GREN_KICK ? STRING_ANIM_GRENADEKICK_SCR : STRING_ANIM_GRENADERETURN_SCR;
|
|
|
|
|
|
|
|
m_pGrenade->setOrigin(GrenadeThrowPoint(origin, orientation[0], csAnim));
|
|
|
|
m_pGrenade->velocity = m_vGrenadeVel;
|
|
|
|
|
|
|
|
m_pGrenade->edict->r.ownerNum = edict->s.number;
|
|
|
|
m_pGrenade->groundentity = NULL;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
void Actor::Begin_Grenade(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-12 18:19:22 +02:00
|
|
|
DoForceActivate();
|
|
|
|
m_csMood = STRING_ALERT;
|
|
|
|
m_csIdleMood = STRING_NERVOUS;
|
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
if (!m_pGrenade) {
|
|
|
|
if (m_Enemy && (m_Enemy->flags & FL_NOTARGET)) {
|
|
|
|
SetThinkState(THINKSTATE_ATTACK, THINKLEVEL_IDLE);
|
|
|
|
} else if (m_Team != TEAM_GERMAN) {
|
|
|
|
SetThinkState(THINKSTATE_IDLE, THINKLEVEL_IDLE);
|
|
|
|
} else {
|
|
|
|
if (!IsTeamMate(static_cast<Sentient *>(G_GetEntity(0)))) {
|
|
|
|
ForceAttackPlayer();
|
|
|
|
}
|
2023-10-12 18:19:22 +02:00
|
|
|
}
|
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_pGrenade->enemy || m_pGrenade->edict->r.ownerNum == entnum) {
|
|
|
|
m_eGrenadeState = AI_GRENSTATE_FLEE;
|
|
|
|
Grenade_Flee();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_pGrenade) {
|
|
|
|
vec3_t vDelta;
|
2023-10-12 18:19:22 +02:00
|
|
|
float fDistSquared;
|
2023-10-22 15:41:19 +02:00
|
|
|
bool bHasThrowTarget;
|
|
|
|
|
2023-10-24 22:55:22 +02:00
|
|
|
bHasThrowTarget = true;
|
2023-10-12 18:19:22 +02:00
|
|
|
if (!m_Enemy) {
|
2023-10-22 15:41:19 +02:00
|
|
|
Sentient *pEnemy = static_cast<Sentient *>(G_GetEntity(m_pGrenade->edict->r.ownerNum));
|
2023-10-12 18:19:22 +02:00
|
|
|
|
|
|
|
if (pEnemy && pEnemy->m_Team != m_Team) {
|
|
|
|
SetEnemyPos(pEnemy->origin);
|
|
|
|
} else {
|
|
|
|
bHasThrowTarget = false;
|
|
|
|
}
|
|
|
|
}
|
2023-10-22 15:41:19 +02:00
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
if (!bHasThrowTarget) {
|
|
|
|
m_eGrenadeState = AI_GRENSTATE_FLEE;
|
|
|
|
Grenade_Flee();
|
|
|
|
return;
|
|
|
|
}
|
2023-10-22 15:41:19 +02:00
|
|
|
|
|
|
|
VectorSub2D(m_vGrenadePos, origin, vDelta);
|
|
|
|
fDistSquared = VectorLength2DSquared(vDelta);
|
|
|
|
|
|
|
|
if (fDistSquared >= Square(256)) {
|
2023-10-12 18:19:22 +02:00
|
|
|
m_eGrenadeState = AI_GRENSTATE_FLEE;
|
|
|
|
Grenade_Flee();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fDistSquared > 16384 && GrenadeWillHurtTeamAt(m_vGrenadePos)) {
|
|
|
|
m_pGrenade->enemy = this;
|
|
|
|
|
|
|
|
m_eGrenadeState = AI_GRENSTATE_MARTYR_ACQUIRE;
|
|
|
|
Grenade_MartyrAcquire();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GrenadeWillHurtTeamAt(m_vLastEnemyPos)) {
|
|
|
|
m_eGrenadeState = AI_GRENSTATE_FLEE;
|
|
|
|
Grenade_Flee();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
Vector vFace = vDelta;
|
|
|
|
if (CanKickGrenade(m_vGrenadePos, m_vLastEnemyPos, vFace, &m_vGrenadeVel)) {
|
2023-10-12 18:19:22 +02:00
|
|
|
m_eGrenadeMode = AI_GREN_KICK;
|
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
m_vKickDir = m_vGrenadeVel;
|
|
|
|
m_vKickDir.z = 0;
|
|
|
|
m_vKickDir.normalizefast();
|
2023-10-12 18:19:22 +02:00
|
|
|
|
|
|
|
m_pGrenade->enemy = this;
|
2023-10-22 15:41:19 +02:00
|
|
|
m_eGrenadeState = AI_GRENSTATE_KICK_ACQUIRE;
|
2023-10-12 18:19:22 +02:00
|
|
|
|
|
|
|
PostponeEvent(EV_Projectile_Explode, 0.25);
|
|
|
|
Grenade_KickAcquire();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!CanGetGrenadeFromAToB(m_vGrenadePos, m_vLastEnemyPos, true, &m_vGrenadeVel, &m_eGrenadeMode)) {
|
|
|
|
m_eGrenadeState = AI_GRENSTATE_FLEE;
|
|
|
|
Grenade_Flee();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_pGrenade->enemy = this;
|
|
|
|
|
|
|
|
m_eGrenadeState = AI_GRENSTATE_THROW_ACQUIRE;
|
|
|
|
PostponeEvent(EV_Projectile_Explode, 0.75);
|
2023-10-22 15:41:19 +02:00
|
|
|
|
|
|
|
Grenade_ThrowAcquire();
|
|
|
|
} else if (m_Enemy && !m_Enemy->IsSubclassOfActor()) {
|
|
|
|
SetThinkState(THINKSTATE_ATTACK, THINKLEVEL_IDLE);
|
|
|
|
} else if (m_Team == TEAM_AMERICAN) {
|
|
|
|
SetThinkState(THINKSTATE_IDLE, THINKLEVEL_IDLE);
|
2023-10-12 18:19:22 +02:00
|
|
|
} else {
|
2023-10-22 15:41:19 +02:00
|
|
|
if (!IsTeamMate((Sentient *)G_GetEntity(0))) {
|
|
|
|
ForceAttackPlayer();
|
2023-10-12 18:19:22 +02:00
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
void Actor::End_Grenade(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-12 18:19:22 +02:00
|
|
|
m_pszDebugState = "";
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
void Actor::Resume_Grenade(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-12 18:19:22 +02:00
|
|
|
if (m_pGrenade) {
|
|
|
|
Begin_Grenade();
|
|
|
|
} else {
|
|
|
|
Grenade_NextThinkState();
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-10-12 20:17:09 +02:00
|
|
|
void Actor::FinishedAnimation_Grenade(void)
|
|
|
|
{
|
|
|
|
switch (m_eGrenadeState) {
|
|
|
|
case AI_GRENSTATE_FLEE:
|
|
|
|
case AI_GRENSTATE_THROW_ACQUIRE:
|
|
|
|
case AI_GRENSTATE_KICK_ACQUIRE:
|
|
|
|
case AI_GRENSTATE_MARTYR_ACQUIRE:
|
|
|
|
case AI_GRENSTATE_MARTYR:
|
|
|
|
case AI_GRENSTATE_FLEE_SUCCESS:
|
|
|
|
case AI_GRENSTATE_FLEE_FAIL:
|
|
|
|
return;
|
|
|
|
case AI_GRENSTATE_THROW:
|
|
|
|
case AI_GRENSTATE_KICK:
|
|
|
|
Grenade_NextThinkState();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
char assertStr[16317] = {0};
|
2024-09-20 21:53:48 +02:00
|
|
|
Q_strncpyz(assertStr, "\"invalid grenade state in FinishedAnimation()\"\n\tMessage: ", sizeof(assertStr));
|
2023-10-12 20:17:09 +02:00
|
|
|
Q_strcat(assertStr, sizeof(assertStr), DumpCallTrace("state = %i", m_eGrenadeState));
|
|
|
|
assert(!assertStr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
void Actor::Think_Grenade(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-12 18:19:22 +02:00
|
|
|
if (m_bEnableEnemy) {
|
|
|
|
UpdateEnemy(200);
|
|
|
|
}
|
2023-10-22 15:41:19 +02:00
|
|
|
|
2023-10-12 18:19:22 +02:00
|
|
|
m_pszDebugState = "";
|
|
|
|
NoPoint();
|
|
|
|
|
|
|
|
if (level.inttime - m_iFirstGrenadeTime > 8000) {
|
|
|
|
Anim_Stand();
|
|
|
|
Grenade_NextThinkState();
|
|
|
|
PostThink(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-10-22 15:41:19 +02:00
|
|
|
if (m_pGrenade) {
|
|
|
|
switch (m_eGrenadeState) {
|
|
|
|
case AI_GRENSTATE_FLEE:
|
|
|
|
case AI_GRENSTATE_FLEE_SUCCESS:
|
|
|
|
case AI_GRENSTATE_FLEE_FAIL:
|
|
|
|
m_pszDebugState = "RunAway";
|
|
|
|
Grenade_Flee();
|
|
|
|
break;
|
|
|
|
case AI_GRENSTATE_THROW_ACQUIRE:
|
|
|
|
m_pszDebugState = "ThrowAcquire";
|
|
|
|
Grenade_ThrowAcquire();
|
|
|
|
break;
|
|
|
|
case AI_GRENSTATE_THROW:
|
|
|
|
m_pszDebugState = "Throw";
|
|
|
|
Grenade_Throw();
|
|
|
|
break;
|
|
|
|
case AI_GRENSTATE_KICK_ACQUIRE:
|
|
|
|
m_pszDebugState = "KickAcquire";
|
|
|
|
Grenade_KickAcquire();
|
|
|
|
break;
|
|
|
|
case AI_GRENSTATE_KICK:
|
|
|
|
m_pszDebugState = "Kick";
|
|
|
|
m_bHasDesiredLookAngles = false;
|
|
|
|
ContinueAnimation();
|
|
|
|
break;
|
|
|
|
case AI_GRENSTATE_MARTYR_ACQUIRE:
|
|
|
|
m_pszDebugState = "MartyrAcquire";
|
|
|
|
Grenade_MartyrAcquire();
|
|
|
|
break;
|
|
|
|
case AI_GRENSTATE_MARTYR:
|
|
|
|
m_pszDebugState = "Martyr";
|
|
|
|
Grenade_Martyr();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
m_pszDebugState = "***Invalid***";
|
|
|
|
char assertStr[16317] = {0};
|
|
|
|
strcpy(assertStr, "\"invalid grenade state\"\n\tMessage: ");
|
|
|
|
Q_strcat(assertStr, sizeof(assertStr), DumpCallTrace("thinkstate = %i", m_State));
|
|
|
|
assert(!assertStr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (m_eGrenadeState && m_eGrenadeState != AI_GRENSTATE_FLEE_FAIL) {
|
|
|
|
if (m_eGrenadeMode == AI_GRENSTATE_FLEE_SUCCESS) {
|
|
|
|
Anim_Attack();
|
2023-10-12 18:19:22 +02:00
|
|
|
} else {
|
2023-10-22 15:41:19 +02:00
|
|
|
Anim_Stand();
|
2023-10-12 18:19:22 +02:00
|
|
|
}
|
2023-10-22 15:41:19 +02:00
|
|
|
Grenade_NextThinkState();
|
|
|
|
} else {
|
|
|
|
m_pszDebugState = "Wait";
|
|
|
|
Grenade_Wait();
|
2023-10-12 18:19:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
PostThink(false);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|