NS/main/source/mod/AvHWelder.cpp
pierow 3d5cb0bc6d Fixed func_breakable and func_weldable permanently blocking building placement
This is a fix for Issue #55. Changes made:

* Server ignores intangible entities when determining if a building placement is valid (fixes func_breakable issue)
* If a func_weldable has the "welds open" spawnflag set, then upon completing the weld when it plays the break effect, it will become fully intangible. It will reset upon round restart.

Fix by @RGreenlees
2023-09-16 11:36:34 -04:00

372 lines
11 KiB
C++

//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. =========
//
// The copyright to the contents herein is the property of Charles G. Cleveland.
// The contents may be used and/or copied only with the written permission of
// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose:
//
// $Workfile: AvHWelder.cpp $
// $Date: 2002/11/22 21:28:17 $
//
//-------------------------------------------------------------------------------
// $Log: AvHWelder.cpp,v $
// Revision 1.23 2002/11/22 21:28:17 Flayra
// - mp_consistency changes
//
// Revision 1.22 2002/11/12 02:29:48 Flayra
// - Change inflictor for better logging
//
// Revision 1.21 2002/11/06 01:39:26 Flayra
// - Damage refactoring (TakeDamage assumes caller has already adjusted for friendly fire, etc.)
//
// Revision 1.20 2002/10/03 18:47:40 Flayra
// - Added heavy view model
//
// Revision 1.19 2002/09/23 22:37:04 Flayra
// - Fixed bug (typo?!) where welder was removing jetpacks (and removing marine status!)
//
// Revision 1.18 2002/08/16 02:49:45 Flayra
// - Updates to allow welder to repair buildables
// - Removed ability to repair soldier armor until it's visible
//
// Revision 1.17 2002/07/26 23:03:08 Flayra
// - New artwork
//
// Revision 1.16 2002/06/25 17:50:31 Flayra
// - Reworking for correct player animations and new enable/disable state, new view model artwork, alien weapon refactoring
//
// Revision 1.15 2002/06/03 16:39:10 Flayra
// - Added different deploy times (this should be refactored a bit more)
//
// Revision 1.14 2002/05/28 17:44:26 Flayra
// - Tweak weapon deploy volume, as Valve's sounds weren't normalized, welder clears webs now, welder no longer makes ambient hum, slight refactoring of variable names away from Valve style
//
// Revision 1.13 2002/05/23 02:32:40 Flayra
// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development.
//
//===============================================================================
#include "AvHMarineWeapons.h"
#include "AvHMarineEquipmentConstants.h"
#include "AvHWeldable.h"
#include "AvHSpecials.h"
#include "MathUtil.h"
#ifdef AVH_SERVER
#include "AvHPlayerUpgrade.h"
#include "AvHServerUtil.h"
#include "AvHPlayer.h"
#include "AvHMarineEquipment.h"
#include "AvHGamerules.h"
extern int gWelderConstEventID;
#endif
int AvHWelder::GetDeployAnimation() const
{
return 3;
}
float AvHWelder::GetDeployTime() const
{
return .55f;
}
char* AvHWelder::GetHeavyViewModel() const
{
return kWelderHVVModel;
}
char* AvHWelder::GetPlayerModel() const
{
return kWelderPModel;
}
int AvHWelder::GetShootAnimation() const
{
return 2;
}
char* AvHWelder::GetViewModel() const
{
return kWelderVModel;
}
char* AvHWelder::GetWorldModel() const
{
return kWelderWModel;
}
void AvHWelder::FireProjectiles(void)
{
#ifdef AVH_SERVER
Vector theWelderBarrel = this->GetWorldBarrelPoint();
UTIL_MakeVectors(this->m_pPlayer->pev->v_angle);
Vector vecEnd = theWelderBarrel + gpGlobals->v_forward*BALANCE_VAR(kWelderRange);
TraceResult tr;
UTIL_TraceLine(theWelderBarrel, vecEnd, dont_ignore_monsters, dont_ignore_glass, ENT( m_pPlayer->pev ), &tr);
float theROF = this->GetRateOfFire();
bool theDidWeld = false;
if(tr.flFraction != 1.0f)
{
// If it's a weldable, check if it's valid for construction
CBaseEntity* theEntity = CBaseEntity::Instance(tr.pHit);
AvHWeldable* theWeldable = dynamic_cast<AvHWeldable*>(theEntity);
if(theWeldable)
{
if(theWeldable->GetCanBeWelded())
{
// Build it by the amount of our rate of fire
theWeldable->AddBuildTime(theROF);
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(this->m_pPlayer);
ASSERT(thePlayer);
thePlayer->TriggerProgressBar(theWeldable->entindex(), 1);
theDidWeld = true;
}
}
// else it's not a weldable, apply damage to it
else
{
if(this->m_pPlayer->pev->team == theEntity->pev->team)
{
if( this->RepairTarget(theEntity, theROF) )
theDidWeld = true;
}
else
{
// Apply damage to it and playback other event
float theScalar = 1.0f;
if(GetGameRules()->CanEntityDoDamageTo(this->m_pPlayer, theEntity, &theScalar))
{
float theDamageMultiplier;
AvHPlayerUpgrade::GetWeaponUpgrade(this->m_pPlayer->pev->iuser3, this->m_pPlayer->pev->iuser4, &theDamageMultiplier);
float theDamage = this->mDamage*theDamageMultiplier*theScalar;
theEntity->TakeDamage(this->pev, this->m_pPlayer->pev, theDamage, DMG_BURN);
}
}
}
if(!theDidWeld)
{
if(this->GetIsWelding())
{
PLAYBACK_EVENT_FULL(0, this->m_pPlayer->edict(), gWelderConstEventID, 0, this->m_pPlayer->pev->origin, (float *)&g_vecZero, 0.0, 0.0, 1, 0, 0, 0 );
this->SetIsWelding(false);
}
}
else
{
if(!this->GetIsWelding())
{
PLAYBACK_EVENT_FULL(0, this->m_pPlayer->edict(), gWelderConstEventID, 0, this->m_pPlayer->pev->origin, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 );
this->SetIsWelding(true);
}
}
}
else
{
if(this->GetIsWelding())
{
PLAYBACK_EVENT_FULL(0, this->m_pPlayer->edict(), gWelderConstEventID, 0, this->m_pPlayer->pev->origin, (float *)&g_vecZero, 0.0, 0.0, 1, 0, 0, 0 );
this->SetIsWelding(false);
}
}
if (!theDidWeld) // Only check for cut strands if we're not welding something already
{
// Scan area for webs, and clear them. I can't make the webs solid, and it seems like the welder might do this, so why not? Also
// adds neat element of specialization where a guy with a welder might be needed to clear an area before an attack, kinda RPS
const float kWebClearingRadius = 75;
const float kWebCuttingDistance = 10.0f;
CBaseEntity* thePotentialWebStrand = NULL;
while ((thePotentialWebStrand = UTIL_FindEntityInSphere(thePotentialWebStrand, theWelderBarrel, kWebClearingRadius)) != NULL)
{
AvHWebStrand* theWebStrand = dynamic_cast<AvHWebStrand*>(thePotentialWebStrand);
if (theWebStrand)
{
//theWebStrand->Break();
Vector WelderCheckPoint;
VectorGetMidPointOnLine(theWelderBarrel, vecEnd, WelderCheckPoint);
Vector ClosestPointOnStrand;
VectorGetClosestPointOnLine(theWebStrand->GetStartPos(), theWebStrand->GetEndPos(), WelderCheckPoint, ClosestPointOnStrand);
float DistCuttingLineToStrand = VectorDistanceFromLine(theWelderBarrel, vecEnd, ClosestPointOnStrand);
if (DistCuttingLineToStrand <= kWebCuttingDistance)
{
theWebStrand->Break();
}
}
}
}
#endif
}
int AvHWelder::GetBarrelLength() const
{
// Note that this variable is used in EV_Welder
return kWelderBarrelLength;
}
float AvHWelder::GetRateOfFire() const
{
return BALANCE_VAR(kWelderROF);
}
void AvHWelder::Holster( int skiplocal )
{
AvHMarineWeapon::Holster(skiplocal);
#ifdef AVH_SERVER
if(this->GetIsWelding())
{
PLAYBACK_EVENT_FULL(0, this->m_pPlayer->edict(), gWelderConstEventID, 0, this->m_pPlayer->pev->origin, (float *)&g_vecZero, 0.0, 0.0, 1, 0, 0, 0 );
this->SetIsWelding(false);
}
#endif
PLAYBACK_EVENT_FULL(0, this->m_pPlayer->edict(), this->mEndEvent, 0, this->m_pPlayer->pev->origin, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 );
this->PlaybackEvent(this->mEndEvent);
}
void AvHWelder::WeaponIdle(void)
{
#ifdef AVH_SERVER
if(this->GetIsWelding() && this->mAttackButtonDownLastFrame)
{
PLAYBACK_EVENT_FULL(0, this->m_pPlayer->edict(), gWelderConstEventID, 0, this->m_pPlayer->pev->origin, (float *)&g_vecZero, 0.0, 0.0, 1, 0, 0, 0 );
this->SetIsWelding(false);
}
#endif
AvHMarineWeapon::WeaponIdle();
}
void AvHWelder::Init()
{
this->mRange = BALANCE_VAR(kWelderRange);
this->mDamage = BALANCE_VAR(kWelderDamage);
this->SetIsWelding(false);
}
BOOL AvHWelder::IsUseable(void)
{
// No ammo, always useable
return TRUE;
}
void AvHWelder::Precache()
{
AvHMarineWeapon::Precache();
PRECACHE_UNMODIFIED_SOUND(kWeldingSound);
PRECACHE_UNMODIFIED_SOUND(kWeldingHitSound);
PRECACHE_UNMODIFIED_SOUND(kWeldingStopSound);
this->mEvent = PRECACHE_EVENT(1, kWelderEventName);
PRECACHE_EVENT(1, kWelderConstEventName);
this->mStartEvent = PRECACHE_EVENT(1, kWelderStartEventName);
this->mEndEvent = PRECACHE_EVENT(1, kWelderEndEventName);
}
#ifdef AVH_SERVER
bool AvHWelder::RepairTarget(CBaseEntity* inEntity, float inROF)
{
int theAmountToRepair = inROF*BALANCE_VAR(kWelderRepairRate);
bool theReturn = false;
if(inEntity)
{
AvHPlayer* theHitPlayer = dynamic_cast<AvHPlayer*>(inEntity);
if(theHitPlayer)
{
// Repair armor if possible
int theCurrentArmor = theHitPlayer->pev->armorvalue;
int theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(theHitPlayer->pev->iuser4, theHitPlayer->GetUser3());
if(theCurrentArmor < theMaxArmor)
{
int theNewArmor = theCurrentArmor + (theAmountToRepair * BALANCE_VAR(kWelderPlayerModifier));
theHitPlayer->pev->armorvalue = min(theMaxArmor, theNewArmor);
theReturn = true;
}
// Burn off webs
if(theHitPlayer->GetIsEnsnared())
{
theHitPlayer->SetEnsnareState(false);
theReturn = true;
}
}
else //if(GetHasUpgrade(inEntity->pev->iuser4, MASK_BUILDABLE))
{
// Max health in armorvalue
AvHBaseBuildable* theBuildable = dynamic_cast<AvHBaseBuildable*>(inEntity);
if(theBuildable)
{
if(theBuildable->Regenerate((theAmountToRepair * BALANCE_VAR(kWelderBuildingModifier)), false))
{
// Award experience for welding the CC. Might award a little more if barely wounded, but that seems OK.
if(GetGameRules()->GetIsCombatMode() && (theBuildable->pev->iuser3 == AVH_USER3_COMMANDER_STATION))
{
AvHPlayer* theWeldingPlayer = dynamic_cast<AvHPlayer*>(this->m_pPlayer);
if(theWeldingPlayer && (theWeldingPlayer->pev->team == theBuildable->pev->team))
{
float theCombatHealExperienceScalar = BALANCE_VAR(kCombatHealExperienceScalar);
theWeldingPlayer->AwardExperienceForObjective(theAmountToRepair*theCombatHealExperienceScalar, theBuildable->GetMessageID());
}
}
theReturn = true;
}
}
}
}
return theReturn;
}
#endif
void AvHWelder::Spawn()
{
AvHMarineWeapon::Spawn();
Precache();
this->m_iId = AVH_WEAPON_WELDER;
//this->m_iDefaultAmmo = kWelderMaxClip;
// Set our class name
this->pev->classname = MAKE_STRING(kwsWelder);
SET_MODEL(ENT(this->pev), kWelderWModel);
FallInit();// get ready to fall down.
}
// When welder is deployed, it makes the welding sound
void AvHWelder::WelderThink()
{
}
bool AvHWelder::UsesAmmo(void) const
{
return false;
}