openmohaa/code/fgame/barrels.cpp

491 lines
14 KiB
C++
Raw Normal View History

2016-03-27 11:49:47 +02:00
/*
===========================================================================
2024-10-21 21:59:12 +02:00
Copyright (C) 2024 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
===========================================================================
*/
// 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
2024-10-21 21:59:12 +02:00
(
2016-03-27 11:49:47 +02:00
"_barrel_think",
EV_DEFAULT,
NULL,
NULL,
"think function for a barrel."
2024-10-21 21:59:12 +02:00
);
2016-03-27 11:49:47 +02:00
Event EV_Barrel_Setup
2024-10-21 21:59:12 +02:00
(
2016-03-27 11:49:47 +02:00
"_barrel_setup",
EV_DEFAULT,
NULL,
NULL,
"Does the post spawn setup of the barrel"
2024-10-21 21:59:12 +02:00
);
2016-03-27 11:49:47 +02:00
Event EV_Barrel_SetType
2024-10-21 21:59:12 +02:00
(
2016-03-27 11:49:47 +02:00
"barreltype",
EV_DEFAULT,
"s",
"type",
"Sets the barrel's type"
2024-10-21 21:59:12 +02:00
);
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 }
2016-03-27 11:49:47 +02:00
};
BarrelObject::BarrelObject()
{
2024-10-21 21:59:12 +02:00
AddWaitTill(STRING_DEATH);
2018-09-17 23:50:38 +02:00
2024-10-21 21:59:12 +02:00
if (LoadingSavegame) {
return;
}
2016-03-27 11:49:47 +02:00
2024-10-21 21:59:12 +02:00
edict->s.eType = ET_GENERAL;
2016-03-27 11:49:47 +02:00
2024-10-21 21:59:12 +02:00
m_iBarrelType = 0;
m_fFluidAmount = 0;
m_fHeightFluid = 0;
2016-03-27 11:49:47 +02:00
2024-10-21 21:59:12 +02:00
for (int i = 0; i < MAX_BARREL_LEAKS; i++) {
m_bLeaksActive[i] = 0;
}
2016-03-27 11:49:47 +02:00
2024-10-21 21:59:12 +02:00
m_fDamageSoundTime = 0;
mass = 500;
max_health = 75;
health = 75;
deadflag = DEAD_NO;
takedamage = DAMAGE_YES;
m_vJitterAngles = vec_zero;
2016-03-27 11:49:47 +02:00
2024-10-21 21:59:12 +02:00
PostEvent(EV_Barrel_Setup, EV_POSTSPAWN);
2016-03-27 11:49:47 +02:00
}
2024-10-21 21:59:12 +02:00
int BarrelObject::PickBarrelLeak(void)
2016-03-27 11:49:47 +02:00
{
2024-10-21 21:59:12 +02:00
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;
2016-03-27 11:49:47 +02:00
}
2024-10-21 21:59:12 +02:00
void BarrelObject::BarrelSetup(Event *ev)
2016-03-27 11:49:47 +02:00
{
2024-10-21 21:59:12 +02:00
setMoveType(MOVETYPE_PUSH);
setSolidType(SOLID_BSP);
2016-03-27 11:49:47 +02:00
2024-10-21 21:59:12 +02:00
m_vStartAngles = angles;
m_fJitterScale = 64.0f / size[2];
2016-03-27 11:49:47 +02:00
2024-10-21 21:59:12 +02:00
m_fFluidAmount = size[0] * size[1] * size[2] / 150.0f;
m_fHeightFluid = m_fFluidAmount / size[2];
2016-03-27 11:49:47 +02:00
2024-10-21 21:59:12 +02:00
// Position the barrel correctly
CheckGround();
2016-03-27 11:49:47 +02:00
}
2024-10-21 21:59:12 +02:00
void BarrelObject::BarrelSetType(Event *ev)
2016-03-27 11:49:47 +02:00
{
2024-10-21 21:59:12 +02:00
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;
2016-03-27 11:49:47 +02:00
}
2024-10-21 21:59:12 +02:00
void BarrelObject::BarrelThink(Event *ev)
2016-03-27 11:49:47 +02:00
{
2024-10-21 21:59:12 +02:00
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));
} else {
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_5));
}
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));
} else {
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_6));
}
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));
} else {
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_7));
}
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));
} else {
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_7));
}
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);
}
}
else {
Vector ang;
2024-10-21 21:59:12 +02:00
ang = m_vStartAngles;
ang[0] += m_vJitterAngles[0];
ang[2] += m_vJitterAngles[2];
setAngles(ang);
2024-10-21 21:59:12 +02:00
if (m_vJitterAngles[0] < 0) {
m_vJitterAngles[0] += 1.f / 3.f * m_fJitterScale;
2024-10-21 21:59:12 +02:00
if (m_vJitterAngles[0] > 0) {
m_vJitterAngles[0] = 0;
}
} else if (m_vJitterAngles[0] > 0) {
m_vJitterAngles[0] -= 1.f / 3.f * m_fJitterScale;
2024-10-21 21:59:12 +02:00
if (m_vJitterAngles[0] < 0) {
m_vJitterAngles[0] = 0;
}
2024-10-21 21:59:12 +02:00
}
m_vJitterAngles[0] = -m_vJitterAngles[0];
2024-10-21 21:59:12 +02:00
if (m_vJitterAngles[2] < 0) {
m_vJitterAngles[2] += 1.f / 3.f * m_fJitterScale;
2024-10-21 21:59:12 +02:00
if (m_vJitterAngles[2] > 0) {
m_vJitterAngles[2] = 0;
}
} else if (m_vJitterAngles[2] > 0) {
m_vJitterAngles[2] -= 1.f / 3.f * m_fJitterScale;
2024-10-21 21:59:12 +02:00
if (m_vJitterAngles[2] < 0) {
m_vJitterAngles[2] = 0;
}
2024-10-21 21:59:12 +02:00
}
m_vJitterAngles[2] = -m_vJitterAngles[2];
}
2024-10-21 21:59:12 +02:00
// 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) {
2024-10-21 21:59:12 +02:00
// big leak
LoopSound("liquid_leak", 1, -1, -1, 1);
} else if (iBiggestLeak & 2) {
// medium leak
LoopSound("liquid_leak", 0.6, -1, -1, 0.9);
} else {
// small leak
LoopSound("liquid_leak", 0.3, -1, -1, 0.8);
2024-10-21 21:59:12 +02:00
}
}
if (i == MAX_BARREL_LEAKS) {
StopLoopSound();
}
if (m_vJitterAngles[0] || m_vJitterAngles[2] || i < MAX_BARREL_LEAKS) {
m_fLastEffectTime += 0.075f;
if (m_fLastEffectTime <= level.time) {
2024-10-21 21:59:12 +02:00
m_fLastEffectTime = level.time + 0.075f;
}
PostEvent(EV_Barrel_Think, m_fLastEffectTime - level.time);
}
2016-03-27 11:49:47 +02:00
}
2024-10-21 21:59:12 +02:00
void BarrelObject::BarrelDamaged(Event *ev)
2016-03-27 11:49:47 +02:00
{
2024-10-21 21:59:12 +02:00
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));
} else {
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_8));
}
} else {
gi.SetBroadcastVisible(vHitPos, vHitPos);
if (m_iBarrelType == BARREL_OIL) {
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_3));
} else {
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_MAKE_EFFECT_7));
}
}
gi.MSG_WriteCoord(vHitPos[0]);
gi.MSG_WriteCoord(vHitPos[1]);
gi.MSG_WriteCoord(vHitPos[2]);
gi.MSG_WriteDir(vHitNormal);
gi.MSG_EndCGM();
}
2016-03-27 11:49:47 +02:00
}
2024-10-21 21:59:12 +02:00
void BarrelObject::BarrelKilled(Event *ev)
2016-03-27 11:49:47 +02:00
{
2024-10-21 21:59:12 +02:00
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);
2016-03-27 11:49:47 +02:00
}