source-engine/game/shared/swarm/asw_weapon_medical_satchel_shared.cpp
2023-10-03 17:23:56 +03:00

368 lines
9.8 KiB
C++

#include "cbase.h"
#include "asw_weapon_medical_satchel_shared.h"
#include "in_buttons.h"
#ifdef CLIENT_DLL
#include "c_asw_player.h"
#include "c_asw_weapon.h"
#include "c_asw_marine.h"
#include "c_asw_game_resource.h"
#include "c_asw_marine_resource.h"
#include "asw_marine_profile.h"
#include "asw_hud_crosshair.h"
#include "asw_input.h"
#else
#include "asw_marine.h"
#include "asw_player.h"
#include "asw_weapon.h"
#include "asw_marine_resource.h"
#include "npcevent.h"
#include "asw_sentry_base.h"
#include "asw_gamerules.h"
#include "asw_fail_advice.h"
#include "asw_marine_speech.h"
#include "asw_triggers.h"
#include "asw_marine_profile.h"
#endif
#include "asw_marine_skills.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define MEDSATCHEL_HEAL_AMOUNT 30
#define MAX_HEAL_DISTANCE 200
#define MAX_HEAL_DISTANCE_SERVER 300
#define THROW_INTERVAL 1.0 // interval between heal applications
IMPLEMENT_NETWORKCLASS_ALIASED( ASW_Weapon_Medical_Satchel, DT_ASW_Weapon_Medical_Satchel )
BEGIN_NETWORK_TABLE( CASW_Weapon_Medical_Satchel, DT_ASW_Weapon_Medical_Satchel )
#ifdef CLIENT_DLL
// recvprops
#else
// sendprops
#endif
END_NETWORK_TABLE()
BEGIN_PREDICTION_DATA( CASW_Weapon_Medical_Satchel )
END_PREDICTION_DATA()
LINK_ENTITY_TO_CLASS( asw_weapon_medical_satchel, CASW_Weapon_Medical_Satchel );
PRECACHE_WEAPON_REGISTER(asw_weapon_medical_satchel);
#ifndef CLIENT_DLL
//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CASW_Weapon_Medical_Satchel )
END_DATADESC()
ConVar asw_marine_special_heal_chatter_chance("asw_marine_special_heal_chatter_chance", "0.05", 0, "Chance of medic saying a special healing line. Wounded special chance is this times 5.");
#endif /* not client */
CASW_Weapon_Medical_Satchel::CASW_Weapon_Medical_Satchel()
{
m_fMinRange1 = 0;
m_fMaxRange1 = 2048;
m_fMinRange2 = 256;
m_fMaxRange2 = 1024;
}
Activity CASW_Weapon_Medical_Satchel::GetPrimaryAttackActivity( void )
{
return ACT_VM_PRIMARYATTACK;
}
void CASW_Weapon_Medical_Satchel::PrimaryAttack( void )
{
// if mouse over entity is a marine
GiveHealth();
}
int CASW_Weapon_Medical_Satchel::GetHealAmount()
{
CASW_Marine *pMarine = GetMarine();
if ( !pMarine )
{
return 0;
}
float flHealAmount = MarineSkills()->GetSkillBasedValueByMarine(pMarine, ASW_MARINE_SKILL_HEALING, ASW_MARINE_SUBSKILL_HEALING_HPS);
//CALL_ATTRIB_HOOK_FLOAT( flHealAmount, mod_heal_bonus );
return flHealAmount;
}
// checks if we're highlighting a fellow marine, if so we chuck him some health
bool CASW_Weapon_Medical_Satchel::GiveHealth()
{
#ifndef CLIENT_DLL
CASW_Player *pOwner = GetCommander();
CASW_Marine *pMarine = GetMarine();
if ( !pOwner || !pMarine )
return false;
CASW_Marine* pTargetMarine = dynamic_cast< CASW_Marine* >( pOwner->GetHighlightEntity() );
if ( !pTargetMarine )
return false;
GiveHealthTo( pTargetMarine );
#else
// Play a cool animation or effect here!
#endif
return true;
}
#ifdef CLIENT_DLL
// if the player has his mouse over another marine, highlight it, cos he's the one we can give health to
void CASW_Weapon_Medical_Satchel::MouseOverEntity(C_BaseEntity *pEnt, Vector vecWorldCursor)
{
C_ASW_Marine* pOtherMarine = C_ASW_Marine::AsMarine( pEnt );
CASW_Player *pOwner = GetCommander();
CASW_Marine *pMarine = GetMarine();
if (!pOwner || !pMarine)
return;
float fOtherHealth = 1.0f;
if (pOtherMarine && pOtherMarine->GetMarineResource())
fOtherHealth = pOtherMarine->GetMarineResource()->GetHealthPercent();
if (!pOtherMarine)
{
C_ASW_Game_Resource *pGameResource = ASWGameResource();
if (pGameResource)
{
// find marine closest to world cursor
const float fMustBeThisClose = 70;
const C_ASW_Game_Resource::CMarineToCrosshairInfo::tuple_t &info = pGameResource->GetMarineCrosshairCache()->GetClosestMarine();
if ( info.m_fDistToCursor <= fMustBeThisClose )
{
pOtherMarine = info.m_hMarine.Get();
}
}
}
// if the marine our cursor is over is near enough, highlight him
if (pOtherMarine)
{
float dist = (pMarine->GetAbsOrigin() - pOtherMarine->GetAbsOrigin()).Length2D();
if (dist < MAX_HEAL_DISTANCE)
{
bool bCanGiveHealth = ( fOtherHealth < 1.0f && m_iClip1 > 0 );
ASWInput()->SetHighlightEntity( pOtherMarine, bCanGiveHealth );
if ( bCanGiveHealth ) // if he needs healing, show the give health cursor
{
CASWHudCrosshair *pCrosshair = GET_HUDELEMENT( CASWHudCrosshair );
if ( pCrosshair )
{
pCrosshair->SetShowGiveHealth(true);
}
}
}
}
}
#else
bool CASW_Weapon_Medical_Satchel::MedSatchelEmpty()
{
return ( m_iClip1 <= 0 && m_iClip2 <= 0 );
}
void CASW_Weapon_Medical_Satchel::GiveHealthTo( CASW_Marine *pTarget )
{
if ( !pTarget || m_iClip1 <= 0 )
return;
if ( pTarget->GetHealth() >= pTarget->GetMaxHealth() ) // already on full health
return;
if ( pTarget->GetHealth() <= 0 ) // target is dead!
return;
if ( pTarget->m_bSlowHeal ) // already healing
return;
CASW_Marine *pMarine = GetMarine();
if ( !pMarine )
return;
if ( pMarine->GetFlags() & FL_FROZEN ) // don't allow this if the marine is frozen
return;
// check if we're close enough
Vector diff = pMarine->GetAbsOrigin() - pTarget->GetAbsOrigin();
if ( diff.Length() > MAX_HEAL_DISTANCE_SERVER ) // add a bit of leeway to account for lag
return;
bool bMedic = pMarine && pMarine->GetMarineProfile() && pMarine->GetMarineProfile()->CanUseFirstAid();
//pMarine->DoAnimationEvent(PLAYERANIMEVENT_HEAL);
WeaponSound(SINGLE);
int iHealAmount = GetHealAmount();
pTarget->AddSlowHeal(iHealAmount, 1, pMarine, this );
// Fire event
IGameEvent * event = gameeventmanager->CreateEvent( "player_heal" );
if ( event )
{
CASW_Player *pPlayer = GetCommander();
event->SetInt( "userid", ( pPlayer ? pPlayer->GetUserID() : 0 ) );
event->SetInt( "entindex", pTarget->entindex() );
gameeventmanager->FireEvent( event );
}
ASWFailAdvice()->OnMarineHealed();
if ( ASWGameRules()->GetInfoHeal() )
{
ASWGameRules()->GetInfoHeal()->OnMarineHealed( GetMarine(), pTarget, this );
}
m_iClip1 -= 1;
if ( bMedic )
{
if (pTarget->IsInfested())
{
float fCurePercent = GetInfestationCureAmount();
// cure infestation
if ( fCurePercent > 0 && fCurePercent < 100 )
{
pTarget->CureInfestation(pMarine, fCurePercent);
}
}
bool bSkipChatter = pTarget->IsInfested();
int iTotalMeds = GetTotalMeds();
if ( m_iClip1 <= 0 )
{
if ( iTotalMeds <= 0 )
{
ASWFailAdvice()->OnMedSatchelEmpty();
pMarine->GetMarineSpeech()->Chatter( CHATTER_MEDS_NONE );
bSkipChatter = true;
}
DestroyIfEmpty( false, true ); // out of medical charges, remove this item
}
else if ( iTotalMeds <= 1 )
{
if ( pMarine->GetMarineSpeech()->Chatter( CHATTER_MEDS_LOW ) )
{
bSkipChatter = true;
}
}
if ( !bSkipChatter )
{
// try and do a special chatter?
bool bSkipChatter = false;
if (pMarine->GetMarineSpeech()->AllowCalmConversations(CONV_HEALING_CRASH))
{
if (!pTarget->m_bDoneWoundedRebuke && pTarget->GetMarineResource() && pTarget->GetMarineResource()->m_bTakenWoundDamage)
{
// marine has been wounded and this is our first heal after the fact - good chance of the medic saying something
if (random->RandomFloat() < asw_marine_special_heal_chatter_chance.GetFloat() * 3)
{
if (CASW_MarineSpeech::StartConversation(CONV_SERIOUS_INJURY, pMarine, pTarget))
{
bSkipChatter = true;
pTarget->m_bDoneWoundedRebuke = true;
}
}
}
// if we didn't complaint about a serious injury, check for doing a different kind of conv
float fRand = random->RandomFloat();
if (!bSkipChatter && fRand < asw_marine_special_heal_chatter_chance.GetFloat())
{
if (pTarget->GetMarineProfile() && pTarget->GetMarineProfile()->m_VoiceType == ASW_VOICE_CRASH
&& pMarine->GetMarineProfile() && pMarine->GetMarineProfile()->m_VoiceType == ASW_VOICE_BASTILLE) // bastille healing crash
{
if (random->RandomFloat() < 0.5f)
{
if (CASW_MarineSpeech::StartConversation(CONV_HEALING_CRASH, pMarine, pTarget))
bSkipChatter = true;
}
else
{
if (CASW_MarineSpeech::StartConversation(CONV_MEDIC_COMPLAIN, pMarine, pTarget))
bSkipChatter = true;
}
}
else
{
if (CASW_MarineSpeech::StartConversation(CONV_MEDIC_COMPLAIN, pMarine, pTarget))
bSkipChatter = true;
}
}
}
if (!bSkipChatter)
pMarine->GetMarineSpeech()->Chatter(CHATTER_HEALING);
}
}
}
float CASW_Weapon_Medical_Satchel::GetInfestationCureAmount()
{
CASW_Marine *pMarine = GetMarine();
if (!pMarine)
return 0.0f;
float flCureAmount = MarineSkills()->GetSkillBasedValueByMarine(pMarine, ASW_MARINE_SKILL_XENOWOUNDS) / 100.0f;
//CALL_ATTRIB_HOOK_FLOAT( flCureAmount, mod_xenowound_bonus );
return flCureAmount;
}
int CASW_Weapon_Medical_Satchel::GetTotalMeds()
{
CASW_Marine *pMarine = GetMarine();
if (!pMarine)
return 0;
int iMeds = 0;
CASW_Weapon_Medical_Satchel* pSatchel = dynamic_cast<CASW_Weapon_Medical_Satchel*>(pMarine->GetWeapon(0));
if (pSatchel)
{
iMeds += pSatchel->Clip1();
}
pSatchel = dynamic_cast<CASW_Weapon_Medical_Satchel*>(pMarine->GetWeapon(1));
if (pSatchel)
{
iMeds += pSatchel->Clip1();
}
return iMeds;
}
bool CASW_Weapon_Medical_Satchel::Holster( CBaseCombatWeapon *pSwitchingTo )
{
bool bResult = BaseClass::Holster( pSwitchingTo );
if ( bResult )
{
DestroyIfEmpty( true, false );
}
return bResult;
}
void CASW_Weapon_Medical_Satchel::Drop( const Vector &vecVelocity )
{
if ( !DestroyIfEmpty( true, false ) )
{
BaseClass::Drop( vecVelocity );
}
}
#endif