openmohaa/code/fgame/barrels.cpp

607 lines
13 KiB
C++
Raw Normal View History

2016-03-27 11:49:47 +02:00
/*
===========================================================================
Copyright (C) 2015 the OpenMoHAA team
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
===========================================================================
*/
// barrels.cpp : Barrels
#include "barrels.h"
#include "weaputils.h"
2023-01-29 20:59:31 +01:00
#include "level.h"
2023-04-29 21:56:38 +02:00
#include "g_phys.h"
2016-03-27 11:49:47 +02:00
/*****************************************************************************
/*QUAKED func_barrel (0 0.25 0.5) ? INDESTRUCTABLE
Brush model barrel object
2024-09-19 10:52:47 +02:00
Bashing only makes thunk or gong sounds (depending on whether it's full or not)
2016-03-27 11:49:47 +02:00
Bullets and explosions effects are dependant apon the type of barrel.
"barreltype" sets the type of barrel that it is. Valid settings are:
- "water" makes the barrel be filled with water.
Always non-volitile. Only destroyed by explosion.
- "oil" makes the barrel be filled with oil.
Leakes oil when shot (not destroyed), shower of flames when exploded.
- "gas" makes the barrel be filled with gas.
A few bullet hits will make it explode.
Explosions will also make it explode.
- "empty" makes the barrel completely empty.
Does nothing special at all. It's just a poor empty barrel. aww :(
******************************************************************************/
Event EV_Barrel_Think
(
"_barrel_think",
EV_DEFAULT,
NULL,
NULL,
"think function for a barrel."
);
Event EV_Barrel_Setup
(
"_barrel_setup",
EV_DEFAULT,
NULL,
NULL,
"Does the post spawn setup of the barrel"
);
Event EV_Barrel_SetType
(
"barreltype",
EV_DEFAULT,
"s",
"type",
"Sets the barrel's type"
);
CLASS_DECLARATION( Entity, BarrelObject, "func_barrel" )
{
{ &EV_Barrel_Setup, &BarrelObject::BarrelSetup },
{ &EV_Barrel_Think, &BarrelObject::BarrelThink },
{ &EV_Barrel_SetType, &BarrelObject::BarrelSetType },
{ &EV_Damage, &BarrelObject::BarrelDamaged },
{ &EV_Killed, &BarrelObject::BarrelKilled },
{ NULL, NULL }
};
BarrelObject::BarrelObject()
{
2018-09-17 23:50:38 +02:00
AddWaitTill(STRING_DEATH);
if (LoadingSavegame)
2016-03-27 11:49:47 +02:00
{
return;
}
edict->s.eType = ET_GENERAL;
m_iBarrelType = 0;
m_fFluidAmount = 0;
m_fHeightFluid = 0;
for( int i = 0; i < MAX_BARREL_LEAKS; i++ )
{
m_bLeaksActive[ i ] = 0;
}
m_fDamageSoundTime = 0;
mass = 500;
max_health = 75;
health = 75;
deadflag = DEAD_NO;
takedamage = DAMAGE_YES;
m_vJitterAngles = vec_zero;
PostEvent( EV_Barrel_Setup, EV_POSTSPAWN );
}
int BarrelObject::PickBarrelLeak
(
void
)
{
int iHighest;
float fHighestHeight;
for( int i = 0; i < MAX_BARREL_LEAKS; i++ )
{
if( !m_bLeaksActive[ i ] ) {
return i;
}
}
fHighestHeight = m_vLeaks[ 0 ][ 2 ];
iHighest = 0;
for( int i = 1; i < MAX_BARREL_LEAKS; i++ )
{
if( m_vLeaks[ 0 ][ 2 ] > fHighestHeight )
{
fHighestHeight = m_vLeaks[ 0 ][ 2 ];
iHighest = i;
}
}
return iHighest;
}
void BarrelObject::BarrelSetup
(
Event *ev
)
{
setMoveType( MOVETYPE_PUSH );
setSolidType( SOLID_BSP );
m_vStartAngles = angles;
m_fJitterScale = 64.0f / size[ 2 ];
m_fFluidAmount = size[ 0 ] * size[ 1 ] * size[ 2 ] / 150.0f;
m_fHeightFluid = m_fFluidAmount / size[ 2 ];
// Position the barrel correctly
CheckGround();
}
void BarrelObject::BarrelSetType
(
Event *ev
)
{
str sType = ev->GetString( 1 );
if( !sType.icmp( "oil" ) )
{
health = 75;
m_iBarrelType = BARREL_OIL;
CacheResource( "models/fx/barrel_empty_destroyed.tik" );
CacheResource( "models/fx/barrel_oil_destroyed.tik" );
}
else if( !sType.icmp( "water" ) )
{
health = 75;
m_iBarrelType = BARREL_WATER;
CacheResource( "models/fx/barrel_empty_destroyed.tik" );
CacheResource( "models/fx/barrel_water_destroyed.tik" );
}
else if( !sType.icmp( "gas" ) )
{
spawnflags &= ~BARREL_INDESTRUCTABLE;
health = 75;
m_iBarrelType = BARREL_GAS;
CacheResource( "models/fx/barrel_gas_destroyed.tik" );
}
else
{
health = 75;
m_iBarrelType = BARREL_EMPTY;
CacheResource( "models/fx/barrel_empty_destroyed.tik" );
}
max_health = health;
}
void BarrelObject::BarrelThink
(
Event *ev
)
{
int i;
int iBiggestLeak;
float fFluidTop;
iBiggestLeak = 0;
fFluidTop = m_fFluidAmount / m_fHeightFluid + ( mins[ 2 ] + origin[ 2 ] );
for( i = 0; i < MAX_BARREL_LEAKS; i++ )
{
if( !m_bLeaksActive[ i ] ) {
continue;
}
// Send infos to clients
if( m_vLeaks[ i ][ 2 ] <= fFluidTop )
{
gi.SetBroadcastVisible( m_vLeaks[ i ], m_vLeaks[ i ] );
if( m_vLeaks[ i ][ 2 ] <= fFluidTop - 1.3f )
{
if( m_vLeaks[ i ][ 2 ] <= fFluidTop - 3.0f )
{
// big leak
if( m_iBarrelType == BARREL_OIL )
{
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_1));
2016-03-27 11:49:47 +02:00
}
else
{
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_5));
2016-03-27 11:49:47 +02:00
}
m_fFluidAmount -= 1.0f;
iBiggestLeak |= 4;
}
else
{
// medium leak
if( m_iBarrelType == BARREL_OIL )
{
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_2));
2016-03-27 11:49:47 +02:00
}
else
{
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_6));
2016-03-27 11:49:47 +02:00
}
m_fFluidAmount -= 0.75f;
iBiggestLeak |= 2;
}
}
else
{
// small leak
if( m_iBarrelType == BARREL_OIL )
{
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_3));
2016-03-27 11:49:47 +02:00
}
else
{
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_7));
2016-03-27 11:49:47 +02:00
}
m_fFluidAmount -= 0.5f;
iBiggestLeak |= 1;
}
gi.MSG_WriteCoord( m_vLeaks[ i ][ 0 ] );
gi.MSG_WriteCoord( m_vLeaks[ i ][ 1 ] );
gi.MSG_WriteCoord( m_vLeaks[ i ][ 2 ] );
gi.MSG_WriteDir( m_vLeakNorms[ i ] );
gi.MSG_EndCGM();
}
else
{
gi.SetBroadcastVisible( m_vLeaks[ i ], m_vLeaks[ i ] );
if( m_iBarrelType == BARREL_OIL )
{
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_3));
2016-03-27 11:49:47 +02:00
}
else
{
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_7));
2016-03-27 11:49:47 +02:00
}
gi.MSG_WriteCoord( m_vLeaks[ i ][ 0 ] );
gi.MSG_WriteCoord( m_vLeaks[ i ][ 1 ] );
gi.MSG_WriteCoord( m_vLeaks[ i ][ 2 ] );
gi.MSG_WriteDir( m_vLeakNorms[ i ] );
gi.MSG_EndCGM();
m_bLeaksActive[ i ] = qfalse;
}
}
if( m_vJitterAngles[ 0 ] == 0.0f && m_vJitterAngles[ 2 ] == 0.0f )
{
if( !VectorCompare( angles, m_vStartAngles ) )
{
setAngles( m_vStartAngles );
}
}
setAngles( Vector( m_vStartAngles[ 0 ] + m_vJitterAngles[ 0 ], m_vStartAngles[ 1 ], m_vStartAngles[ 2 ] + m_vJitterAngles[ 2 ] ) );
if( m_vJitterAngles[ 0 ] > 0.0f )
{
2016-08-02 16:54:32 +02:00
m_vJitterAngles[ 0 ] -= 1.f / 3.f * m_fJitterScale;
2016-03-27 11:49:47 +02:00
if( m_vJitterAngles[ 0 ] > 0.0f )
{
m_vJitterAngles[ 0 ] = 0.0f;
}
}
else if( m_vJitterAngles[ 0 ] < 0.0f )
{
2016-08-02 16:54:32 +02:00
m_vJitterAngles[ 0 ] += 1.f / 3.f * m_fJitterScale;
2016-03-27 11:49:47 +02:00
if( m_vJitterAngles[ 0 ] < 0.0f )
{
m_vJitterAngles[ 0 ] = 0.0f;
}
}
m_vJitterAngles[ 0 ] = -m_vJitterAngles[ 0 ];
if( m_vJitterAngles[ 2 ] > 0.0f )
{
2016-08-02 16:54:32 +02:00
m_vJitterAngles[ 2 ] -= 1.f / 3.f * m_fJitterScale;
2016-03-27 11:49:47 +02:00
if( m_vJitterAngles[ 2 ] > 0.0f )
{
m_vJitterAngles[ 2 ] = 0.0f;
}
}
else if( m_vJitterAngles[ 2 ] < 0.0f )
{
2016-08-02 16:54:32 +02:00
m_vJitterAngles[ 2 ] += 1.f / 3.f * m_fJitterScale;
2016-03-27 11:49:47 +02:00
if( m_vJitterAngles[ 2 ] < 0.0f )
{
m_vJitterAngles[ 2 ] = 0.0f;
}
}
m_vJitterAngles[ 2 ] = -m_vJitterAngles[ 2 ];
// Check for at least one active leak to play a sound
for( i = 0; i < MAX_BARREL_LEAKS; i++ )
{
if( m_bLeaksActive[ i ] ) {
break;
}
}
// Play a leak sound
if( i != MAX_BARREL_LEAKS && iBiggestLeak )
{
if( !( iBiggestLeak & 4 ) )
{
if( iBiggestLeak & 2 )
{
// medium leak
LoopSound( "liquid_leak", 0.60f, -1.0f, -1.0f, 0.90f );
}
else
{
// small leak
LoopSound( "liquid_leak", 0.30f, -1.0f, -1.0f, 0.80f );
}
}
else
{
// big leak
LoopSound( "liquid_leak", 1.0f, -1.0f, -1.0f, 1.0f );
}
}
if( i == MAX_BARREL_LEAKS ) {
StopLoopSound();
}
if( m_vJitterAngles[ 0 ] || m_vJitterAngles[ 2 ] || i < MAX_BARREL_LEAKS )
{
m_fLastEffectTime += 0.075f;
if( level.time >= m_fLastEffectTime )
{
m_fLastEffectTime = level.time + 0.075f;
}
PostEvent( EV_Barrel_Think, m_fLastEffectTime - level.time );
}
}
void BarrelObject::BarrelDamaged
(
Event *ev
)
{
Vector vDir;
Vector vForward;
Vector vRight;
int iDamage;
int iMeansOfDeath;
Vector vHitPos;
Vector vHitDirection;
Vector vHitNormal;
if( !takedamage )
{
return;
}
iDamage = ev->GetInteger( 2 );
iMeansOfDeath = ev->GetInteger( 9 );
vHitPos = ev->GetVector( 4 );
vHitDirection = ev->GetVector( 5 );
vHitNormal = ev->GetVector( 6 );
vDir = ( vHitDirection - vHitNormal ) * 0.5f;
AngleVectors( angles, vForward, vRight, NULL );
m_vJitterAngles[ 0 ] += DotProduct( vDir, vForward ) * m_fJitterScale * 0.0275f * ( float )iDamage;
m_vJitterAngles[ 2 ] += DotProduct( vDir, vRight ) * m_fJitterScale * 0.0275f * ( float )iDamage;
if( m_vJitterAngles[ 0 ] > m_fJitterScale * 1.5f )
{
m_vJitterAngles[ 0 ] = m_fJitterScale * 1.5f;
}
else if( m_vJitterAngles[ 0 ] < -( m_fJitterScale * 1.5f ) )
{
m_vJitterAngles[ 0 ] = -( m_fJitterScale * 1.5f );
}
if( m_vJitterAngles[ 2 ] > m_fJitterScale * 1.5f )
{
m_vJitterAngles[ 2 ] = m_fJitterScale * 1.5f;
}
else if( m_vJitterAngles[ 2 ] < -( m_fJitterScale * 1.5f ) )
{
m_vJitterAngles[ 2 ] = -( m_fJitterScale * 1.5f );
}
if( !( spawnflags & BARREL_INDESTRUCTABLE ) )
{
if( ( iMeansOfDeath == MOD_VEHICLE || iMeansOfDeath == MOD_ROCKET ||
iMeansOfDeath == MOD_GRENADE || iMeansOfDeath == MOD_EXPLODEWALL ||
iMeansOfDeath == MOD_EXPLOSION || m_iBarrelType == BARREL_GAS ) &&
iMeansOfDeath != MOD_BASH
)
{
if( iDamage >= health )
{
PostEvent( EV_Killed, 0.01f );
takedamage = DAMAGE_NO;
return;
}
if( m_iBarrelType == BARREL_GAS )
{
iDamage /= 2;
health -= iDamage;
if( health < 1.0f )
health = 1.0f;
}
}
}
if( !EventPending( EV_Barrel_Think ) )
{
m_fLastEffectTime = level.time - 0.075f;
ProcessEvent( EV_Barrel_Think );
}
if( m_iBarrelType <= BARREL_WATER )
{
int index = PickBarrelLeak();
if( vHitPos[ 2 ] <= m_fFluidAmount / m_fHeightFluid + origin[ 2 ] + mins[ 2 ] )
{
m_bLeaksActive[ index ] = qtrue;
m_vLeaks[ index ] = vHitPos;
m_vLeakNorms[ index ] = vHitNormal;
gi.SetBroadcastVisible( vHitPos, vHitPos );
if( m_iBarrelType == BARREL_OIL )
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_4));
2016-03-27 11:49:47 +02:00
else
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_8));
2016-03-27 11:49:47 +02:00
}
else
{
gi.SetBroadcastVisible( vHitPos, vHitPos );
if( m_iBarrelType == BARREL_OIL )
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_3));
2016-03-27 11:49:47 +02:00
else
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_7));
2016-03-27 11:49:47 +02:00
}
gi.MSG_WriteCoord( vHitPos[ 0 ] );
gi.MSG_WriteCoord( vHitPos[ 1 ] );
gi.MSG_WriteCoord( vHitPos[ 2 ] );
gi.MSG_WriteDir( vHitNormal );
gi.MSG_EndCGM();
}
}
void BarrelObject::BarrelKilled
(
Event *ev
)
{
float fFluidTop;
Vector vPos;
str sModel;
setSolidType( SOLID_NOT );
PostEvent( EV_Remove, 0.05f );
vPos[ 2 ] = ( mins[ 2 ] + origin[ 2 ] );
fFluidTop = m_fFluidAmount / m_fHeightFluid + vPos[ 2 ];
if( m_iBarrelType == BARREL_GAS )
{
RadiusDamage(
centroid,
this,
this,
200,
this,
MOD_EXPLOSION,
350,
24 );
sModel = "models/fx/barrel_gas_destroyed.tik";
}
else if( m_iBarrelType == BARREL_WATER )
{
if( vPos[ 2 ] + 0.25f * maxs[ 2 ] <= fFluidTop )
{
sModel = "models/fx/barrel_water_destroyed.tik";
}
else
{
sModel = "models/fx/barrel_empty_destroyed.tik";
}
}
else if( m_iBarrelType == BARREL_OIL )
{
if( vPos[ 2 ] + 0.25f * maxs[ 2 ] > fFluidTop )
{
sModel = "models/fx/barrel_empty_destroyed.tik";
}
else
{
RadiusDamage(
centroid,
this,
this,
200,
this,
MOD_EXPLOSION,
350,
24 );
sModel = "models/fx/barrel_oil_destroyed.tik";
}
}
else
{
sModel = "models/fx/barrel_empty_destroyed.tik";
}
Animate *exp = new Animate;
// Spawn an explosion effect
exp->edict->s.renderfx |= RF_DONTDRAW;
exp->setModel( sModel );
vPos = origin + ( maxs + mins ) * 0.5f;
exp->setAngles( Vector( -90, 0, 0 ) );
exp->setOrigin( vPos );
exp->NewAnim( "idle" );
exp->PostEvent( EV_Remove, 0.1f );
exp->Unregister( STRING_DEATH );
}