CPlane done

This commit is contained in:
aap 2020-07-19 00:37:44 +02:00
parent a1412d1cdb
commit e74b569115
7 changed files with 211 additions and 91 deletions

View File

@ -19,6 +19,18 @@
#include "World.h"
void
CPlaneTrails::RegisterPoint(CVector pos, uint32 id)
{
// TODO
}
void
CPlaneBanners::RegisterPoint(CVector pos, uint32 id)
{
// TODO
}
bool CSmokeTrails::CigOn = false;
CSmokeTrail CSmokeTrails::aSmoke[3];

View File

@ -2,6 +2,56 @@
#include "common.h"
#include "Vector.h"
// TODO
class CScriptPath
{
public:
};
// TODO
class CScriptPaths
{
public:
};
// TODO
class CPlaneTrail
{
public:
};
// TODO
class CPlaneTrails
{
public:
static void RegisterPoint(CVector pos, uint32 id);
};
// TODO
class CPlaneBanner
{
public:
};
// TODO
class CPlaneBanners
{
public:
static void RegisterPoint(CVector pos, uint32 id);
};
// TODO
class CEscalators
{
public:
};
// TODO
class CEscalator
{
public:
};
class CMovingThing
{
public:

View File

@ -851,7 +851,7 @@ CBoat::BlowUpCar(CEntity *culprit)
bLightsOn = false;
ChangeLawEnforcerState(false);
CExplosion::AddExplosion(this, culprit, EXPLOSION_HELI, GetPosition(), 0);
CExplosion::AddExplosion(this, culprit, EXPLOSION_BOAT, GetPosition(), 0);
CDarkel::RegisterCarBlownUpByPlayer(this);
if(m_aBoatNodes[BOAT_MOVING] == nil)
return;

View File

@ -2,6 +2,7 @@
#include "main.h"
#include "General.h"
#include "CutsceneMgr.h"
#include "ModelIndices.h"
#include "FileMgr.h"
#include "Streaming.h"
@ -12,10 +13,13 @@
#include "Coronas.h"
#include "Particle.h"
#include "Explosion.h"
#include "Fluff.h"
#include "World.h"
#include "HandlingMgr.h"
#include "Plane.h"
//--MIAMI: file done
CPlaneNode *pPathNodes;
CPlaneNode *pPath2Nodes;
CPlaneNode *pPath3Nodes;
@ -39,12 +43,10 @@ CPlaneInterpolationLine aPlaneLineBits[6];
float PlanePathPosition[3];
float OldPlanePathPosition[3];
float PlanePathSpeed[3];
float PlanePath2Position[3];
float PlanePath3Position;
float PlanePath4Position;
float PlanePath2Speed[3];
float PlanePath3Speed;
float PlanePath4Speed;
float PlanePath2Position[5];
float PlanePath3Position[4];
float PlanePath2Speed[5];
float PlanePath3Speed[4];
enum
@ -62,7 +64,6 @@ int32 DropOffCesnaMissionStatus;
int32 DropOffCesnaMissionStartTime;
CPlane *pDropOffCesna;
CPlane::CPlane(int32 id, uint8 CreatedBy)
: CVehicle(CreatedBy)
{
@ -80,14 +81,13 @@ CPlane::CPlane(int32 id, uint8 CreatedBy)
m_bHasBeenHit = false;
m_bIsDrugRunCesna = false;
m_bIsDropOffCesna = false;
m_bTempPlane = false;
SetStatus(STATUS_PLANE);
bIsBIGBuilding = true;
m_level = LEVEL_GENERIC;
#ifdef FIX_BUGS
m_isFarAway = true;
#endif
m_isFarAway = false;
}
CPlane::~CPlane()
@ -123,6 +123,14 @@ CPlane::ProcessControl(void)
int i;
CVector pos;
if(CReplay::IsPlayingBack())
return;
if(GetModelIndex() == MI_AIRTRAIN)
CPlaneTrails::RegisterPoint(GetPosition(), m_nPlaneId);
else if(GetModelIndex() == MI_DEADDODO)
CPlaneBanners::RegisterPoint(GetPosition(), m_nPlaneId);
// Explosion
if(m_bHasBeenHit){
// BUG: since this is all based on frames, you can skip the explosion processing when you go into the menu
@ -154,7 +162,7 @@ CPlane::ProcessControl(void)
int f = ++nFrameGen & 3;
CParticle::AddParticle(PARTICLE_HELI_DEBRIS, GetMatrix() * CVector(0.0f, 0.0f, 0.0f), dir,
nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f),
colors[nFrameGen], rotSpeed, 0, f, 0);
colors[nFrameGen&7], rotSpeed, 0, f, 0);
}
}
if(frm >= 40 && frm <= 80 && frm & 1){
@ -197,17 +205,18 @@ CPlane::ProcessControl(void)
colors[6] = CRGBA(0, 0, 0, 255);
colors[7] = CRGBA(252, 66, 66, 255);
CVector dir;
for(i = 0; i < 40; i++){
dir.x = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f);
dir.y = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f);
dir.z = CGeneral::GetRandomNumberInRange(0.0f, 2.0f);
int rotSpeed = CGeneral::GetRandomNumberInRange(30.0f, 20.0f);
if(CGeneral::GetRandomNumber() & 1)
rotSpeed = -rotSpeed;
int f = ++nFrameGen & 3;
CParticle::AddParticle(PARTICLE_HELI_DEBRIS, GetMatrix() * CVector(0.0f, 0.0f, 0.0f),
CVector(CGeneral::GetRandomNumberInRange(-2.0f, 2.0f),
CGeneral::GetRandomNumberInRange(-2.0f, 2.0f),
CGeneral::GetRandomNumberInRange(0.0f, 2.0f)),
CParticle::AddParticle(PARTICLE_HELI_DEBRIS, GetMatrix() * CVector(0.0f, 0.0f, 0.0f), dir,
nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f),
colors[nFrameGen], rotSpeed, 0, f, 0);
colors[nFrameGen&7], rotSpeed, 0, f, 0);
}
}
if(frm >= 40 && frm <= 60 && frm & 1){
@ -226,7 +235,7 @@ CPlane::ProcessControl(void)
}
if(frm == 30)
bRenderScorched = true;
if(frm == 61){
if(frm == 62){
TheCamera.SetFadeColour(200, 200, 200);
TheCamera.Fade(0.0f, FADE_OUT);
TheCamera.ProcessFade();
@ -363,7 +372,7 @@ CPlane::ProcessControl(void)
CVector posFront2 = (1.0f - f)*pPathNodes[curPathNodeFront2].p + f*pPathNodes[nextPathNodeFront2].p;
// Now set matrix
GetMatrix().GetPosition() = (posRear + posFront) / 2.0f;
GetMatrix().SetTranslateOnly((posRear + posFront) / 2.0f);
GetMatrix().GetPosition().z += 4.3f;
CVector fwd = posFront - posRear;
fwd.Normalise();
@ -382,13 +391,12 @@ CPlane::ProcessControl(void)
GetMatrix().GetRight() = right;
GetMatrix().GetUp() = up;
GetMatrix().GetForward() = fwd;
// Set speed
m_vecMoveSpeed = fwd*PlanePathSpeed[m_nPlaneId]/60.0f;
m_fSpeed = PlanePathSpeed[m_nPlaneId]/60.0f;
m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f);
m_isFarAway = !((posFront - TheCamera.GetPosition()).Magnitude2D() < sq(300.0f));
m_isFarAway = !((posFront - TheCamera.GetPosition()).MagnitudeSqr2D() < sq(300.0f));
}else{
float planePathPosition;
float totalLengthOfFlightPath;
@ -396,26 +404,12 @@ CPlane::ProcessControl(void)
float planePathSpeed;
int numPathNodes;
if(m_bIsDrugRunCesna){
planePathPosition = PlanePath3Position;
if(GetModelIndex() == MI_CHOPPER){
planePathPosition = PlanePath3Position[m_nPlaneId];
totalLengthOfFlightPath = TotalLengthOfFlightPath3;
pathNodes = pPath3Nodes;
planePathSpeed = PlanePath3Speed;
planePathSpeed = PlanePath3Speed[m_nPlaneId];
numPathNodes = NumPath3Nodes;
if(CesnaMissionStatus == CESNA_STATUS_LANDED){
pDrugRunCesna = nil;
FlagToDestroyWhenNextProcessed();
}
}else if(m_bIsDropOffCesna){
planePathPosition = PlanePath4Position;
totalLengthOfFlightPath = TotalLengthOfFlightPath4;
pathNodes = pPath4Nodes;
planePathSpeed = PlanePath4Speed;
numPathNodes = NumPath4Nodes;
if(DropOffCesnaMissionStatus == CESNA_STATUS_LANDED){
pDropOffCesna = nil;
FlagToDestroyWhenNextProcessed();
}
}else{
planePathPosition = PlanePath2Position[m_nPlaneId];
totalLengthOfFlightPath = TotalLengthOfFlightPath2;
@ -484,7 +478,7 @@ CPlane::ProcessControl(void)
f = (pathPositionFront - pathNodes[curPathNodeFront].t)/dist;
CVector posFront = (1.0f - f)*pathNodes[curPathNodeFront].p + f*pathNodes[nextPathNodeFront].p;
// And for another point 60 units in front of the plane, used to calculate roll
// And for another point 30 units in front of the plane, used to calculate roll
float pathPositionFront2 = pathPositionFront + 30.0f;
if(pathPositionFront2 > totalLengthOfFlightPath)
pathPositionFront2 -= totalLengthOfFlightPath;
@ -515,7 +509,7 @@ CPlane::ProcessControl(void)
CVector posFront2 = (1.0f - f)*pathNodes[curPathNodeFront2].p + f*pathNodes[nextPathNodeFront2].p;
// Now set matrix
GetMatrix().GetPosition() = (posRear + posFront) / 2.0f;
GetMatrix().SetTranslateOnly((posRear + posFront) / 2.0f);
GetMatrix().GetPosition().z += 1.0f;
CVector fwd = posFront - posRear;
fwd.Normalise();
@ -535,7 +529,7 @@ CPlane::ProcessControl(void)
m_fSpeed = planePathSpeed/60.0f;
m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f);
m_isFarAway = !((posFront - TheCamera.GetPosition()).Magnitude2D() < sq(300.0f));
m_isFarAway = !((posFront - TheCamera.GetPosition()).MagnitudeSqr2D() < sq(300.0f));
}
}
@ -639,7 +633,8 @@ CPlane::PreRender(void)
void
CPlane::Render(void)
{
CEntity::Render();
if(!CCutsceneMgr::IsRunning())
CEntity::Render();
}
#define CRUISE_SPEED (50.0f)
@ -657,11 +652,9 @@ CPlane::InitPlanes(void)
pPathNodes = LoadPath("data\\paths\\flight.dat", NumPathNodes, TotalLengthOfFlightPath, true);
// Figure out which nodes are on ground
CColPoint colpoint;
CEntity *entity;
for(i = 0; i < NumPathNodes; i++){
if(CWorld::ProcessVerticalLine(pPathNodes[i].p, 1000.0f, colpoint, entity, true, false, false, false, true, false, nil)){
pPathNodes[i].p.z = colpoint.point.z;
if(pPathNodes[i].p.z < 14.0f){
pPathNodes[i].p.z = 14.0f;
pPathNodes[i].bOnGround = true;
}else
pPathNodes[i].bOnGround = false;
@ -688,7 +681,7 @@ CPlane::InitPlanes(void)
aPlaneLineBits[0].position = position;
aPlaneLineBits[0].speed = TAXI_SPEED;
aPlaneLineBits[0].acceleration = 0.0f;
float dist = (TakeOffPoint-600.0f) - position;
float dist = (TakeOffPoint-500.0f) - position;
time += dist/TAXI_SPEED;
position += dist;
@ -697,9 +690,9 @@ CPlane::InitPlanes(void)
aPlaneLineBits[1].time = time;
aPlaneLineBits[1].position = position;
aPlaneLineBits[1].speed = TAXI_SPEED;
aPlaneLineBits[1].acceleration = 33.0f/32.0f;
time += 600.0f/((CRUISE_SPEED+TAXI_SPEED)/2.0f);
position += 600.0f;
aPlaneLineBits[1].acceleration = 618.75f/500.0f;
time += 500.0f/((CRUISE_SPEED+TAXI_SPEED)/2.0f);
position += 500.0f;
// Fly at cruise speed
aPlaneLineBits[2].type = 1;
@ -716,9 +709,9 @@ CPlane::InitPlanes(void)
aPlaneLineBits[3].time = time;
aPlaneLineBits[3].position = position;
aPlaneLineBits[3].speed = CRUISE_SPEED;
aPlaneLineBits[3].acceleration = -33.0f/32.0f;
time += 600.0f/((CRUISE_SPEED+TAXI_SPEED)/2.0f);
position += 600.0f;
aPlaneLineBits[3].acceleration = -618.75f/500.0f;
time += 500.0f/((CRUISE_SPEED+TAXI_SPEED)/2.0f);
position += 500.0f;
// Taxi
aPlaneLineBits[4].type = 1;
@ -739,20 +732,12 @@ CPlane::InitPlanes(void)
TotalDurationOfFlightPath2 = TotalLengthOfFlightPath2/CRUISE_SPEED;
}
/*
// Mission Cesna
// Heli
if(pPath3Nodes == nil){
pPath3Nodes = LoadPath("data\\paths\\flight3.dat", NumPath3Nodes, TotalLengthOfFlightPath3, false);
TotalDurationOfFlightPath3 = TotalLengthOfFlightPath3/CRUISE_SPEED;
}
// Mission Cesna
if(pPath4Nodes == nil){
pPath4Nodes = LoadPath("data\\paths\\flight4.dat", NumPath4Nodes, TotalLengthOfFlightPath4, false);
TotalDurationOfFlightPath4 = TotalLengthOfFlightPath4/CRUISE_SPEED;
}
*/
CStreaming::LoadAllRequestedModels(false);
CStreaming::RequestModel(MI_AIRTRAIN, 0);
CStreaming::LoadAllRequestedModels(false);
@ -766,20 +751,6 @@ CPlane::InitPlanes(void)
plane->m_nCurPathNode = 0;
CWorld::Add(plane);
}
CStreaming::RequestModel(MI_DEADDODO, 0);
CStreaming::LoadAllRequestedModels(false);
for(i = 0; i < 3; i++){
CPlane *plane = new CPlane(MI_DEADDODO, PERMANENT_VEHICLE);
plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f);
plane->SetStatus(STATUS_ABANDONED);
plane->bIsLocked = true;
plane->m_nPlaneId = i;
plane->m_nCurPathNode = 0;
CWorld::Add(plane);
}
}
void
@ -811,7 +782,6 @@ CPlane::LoadPath(char const *filename, int32 &numNodes, float &totalLength, bool
CPlaneNode *nodes = new CPlaneNode[numNodes];
for(i = 0; i < numNodes; i++){
*gString = '\0';
for(lp = 0; work_buff[bp] != '\n' && work_buff[bp] != '\0'; bp++, lp++)
gString[lp] = work_buff[bp];
bp++;
@ -833,6 +803,10 @@ CPlane::LoadPath(char const *filename, int32 &numNodes, float &totalLength, bool
return nodes;
}
int32 LastTimeInPlane, LastTimeNotInPlane;
bool bCesnasActivated;
bool bHelisActivated;
void
CPlane::UpdatePlanes(void)
{
@ -875,25 +849,87 @@ CPlane::UpdatePlanes(void)
t = TotalDurationOfFlightPath2/0x80000;
PlanePath2Position[0] = CRUISE_SPEED * (time & 0x7FFFF)*t;
PlanePath2Position[1] = CRUISE_SPEED * ((time + 0x80000/3) & 0x7FFFF)*t;
PlanePath2Position[2] = CRUISE_SPEED * ((time + 0x80000/3*2) & 0x7FFFF)*t;
PlanePath2Position[1] = CRUISE_SPEED * ((time + 0x80000/5) & 0x7FFFF)*t;
PlanePath2Position[2] = CRUISE_SPEED * ((time + 0x80000/5*2) & 0x7FFFF)*t;
PlanePath2Position[3] = CRUISE_SPEED * ((time + 0x80000/5*3) & 0x7FFFF)*t;
PlanePath2Position[4] = CRUISE_SPEED * ((time + 0x80000/5*4) & 0x7FFFF)*t;
PlanePath2Speed[0] = CRUISE_SPEED*t;
PlanePath2Speed[1] = CRUISE_SPEED*t;
PlanePath2Speed[2] = CRUISE_SPEED*t;
PlanePath2Speed[3] = CRUISE_SPEED*t;
PlanePath2Speed[4] = CRUISE_SPEED*t;
if(CesnaMissionStatus == CESNA_STATUS_FLYING){
PlanePath3Speed = CRUISE_SPEED*TotalDurationOfFlightPath3/0x20000;
PlanePath3Position = PlanePath3Speed * ((time - CesnaMissionStartTime) & 0x1FFFF);
if(time - CesnaMissionStartTime >= 128072)
CesnaMissionStatus = CESNA_STATUS_LANDED;
}
t = TotalDurationOfFlightPath3/0x80000;
PlanePath3Position[0] = CRUISE_SPEED * (time & 0x7FFFF)*t;
PlanePath3Position[1] = CRUISE_SPEED * ((time + 0x80000/4) & 0x7FFFF)*t;
PlanePath3Position[2] = CRUISE_SPEED * ((time + 0x80000/4*2) & 0x7FFFF)*t;
PlanePath3Position[3] = CRUISE_SPEED * ((time + 0x80000/4*3) & 0x7FFFF)*t;
PlanePath3Speed[0] = CRUISE_SPEED*t;
PlanePath3Speed[1] = CRUISE_SPEED*t;
PlanePath3Speed[2] = CRUISE_SPEED*t;
PlanePath3Speed[3] = CRUISE_SPEED*t;
if(DropOffCesnaMissionStatus == CESNA_STATUS_FLYING){
PlanePath4Speed = CRUISE_SPEED*TotalDurationOfFlightPath4/0x80000;
PlanePath4Position = PlanePath4Speed * ((time - DropOffCesnaMissionStartTime) & 0x7FFFF);
if(time - DropOffCesnaMissionStartTime >= 521288)
DropOffCesnaMissionStatus = CESNA_STATUS_LANDED;
if(FindPlayerVehicle() && (FindPlayerVehicle()->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI ||
FindPlayerVehicle()->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE))
LastTimeInPlane = CTimer::GetTimeInMilliseconds();
else
LastTimeNotInPlane = CTimer::GetTimeInMilliseconds();
if(CTimer::GetTimeInMilliseconds() - LastTimeNotInPlane > 10000){
if(!bCesnasActivated){
if(CStreaming::HasModelLoaded(MI_DEADDODO)){
for(i = 0; i < 5; i++){
CPlane *plane = new CPlane(MI_DEADDODO, PERMANENT_VEHICLE);
plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f);
plane->SetStatus(STATUS_ABANDONED);
plane->bIsLocked = true;
plane->m_nPlaneId = i;
plane->m_nCurPathNode = 0;
plane->m_bTempPlane = true;
CWorld::Add(plane);
}
bCesnasActivated = true;
}else
CStreaming::RequestModel(MI_DEADDODO, 0);
}
if(!bHelisActivated){
if(CStreaming::HasModelLoaded(MI_CHOPPER)){
for(i = 0; i < 4; i++){
CPlane *plane = new CPlane(MI_CHOPPER, PERMANENT_VEHICLE);
plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f);
plane->SetStatus(STATUS_ABANDONED);
plane->bIsLocked = true;
plane->m_nPlaneId = i;
plane->m_nCurPathNode = 0;
plane->m_bTempPlane = true;
CWorld::Add(plane);
}
bHelisActivated = true;
}else
CStreaming::RequestModel(MI_CHOPPER, 0);
}
}else if(CTimer::GetTimeInMilliseconds() - LastTimeInPlane > 10000)
RemoveTemporaryPlanes();
}
void
CPlane::RemoveTemporaryPlanes(void)
{
int i;
if(!bHelisActivated && !bCesnasActivated)
return;
i = CPools::GetVehiclePool()->GetSize();
while(--i >= 0){
CPlane *plane = (CPlane*)CPools::GetVehiclePool()->GetSlot(i);
if(plane && plane->IsPlane() && plane->m_bTempPlane){
CWorld::Remove(plane);
delete plane;
}
}
bCesnasActivated = false;
bHelisActivated = false;
}
bool
@ -921,6 +957,7 @@ CPlane::TestRocketCollision(CVector *rocketPos)
return false;
}
//--MIAMI: unused
// BUG: not in CPlane in the game
void
CPlane::CreateIncomingCesna(void)
@ -944,6 +981,7 @@ CPlane::CreateIncomingCesna(void)
printf("CPlane::CreateIncomingCesna(void)\n");
}
//--MIAMI: unused
void
CPlane::CreateDropOffCesna(void)
{
@ -966,8 +1004,21 @@ CPlane::CreateDropOffCesna(void)
printf("CPlane::CreateDropOffCesna(void)\n");
}
//--MIAMI: all unused
const CVector CPlane::FindDrugPlaneCoordinates(void) { return pDrugRunCesna->GetPosition(); }
const CVector CPlane::FindDropOffCesnaCoordinates(void) { return pDropOffCesna->GetPosition(); }
bool CPlane::HasCesnaLanded(void) { return CesnaMissionStatus == CESNA_STATUS_LANDED; }
bool CPlane::HasCesnaBeenDestroyed(void) { return CesnaMissionStatus == CESNA_STATUS_DESTROYED; }
bool CPlane::HasDropOffCesnaBeenShotDown(void) { return DropOffCesnaMissionStatus == CESNA_STATUS_DESTROYED; }
void
CPlane::Load(void)
{
RemoveTemporaryPlanes();
}
void
CPlane::Save(void)
{
RemoveTemporaryPlanes();
}

View File

@ -29,7 +29,6 @@ struct CPlaneInterpolationLine
class CPlane : public CVehicle
{
public:
// 0x288
int16 m_nPlaneId;
int16 m_isFarAway;
int16 m_nCurPathNode;
@ -38,6 +37,7 @@ public:
bool m_bHasBeenHit;
bool m_bIsDrugRunCesna;
bool m_bIsDropOffCesna;
bool m_bTempPlane;
CPlane(int32 id, uint8 CreatedBy);
~CPlane(void);
@ -53,6 +53,7 @@ public:
static void InitPlanes(void);
static void Shutdown(void);
static CPlaneNode *LoadPath(char const *filename, int32 &numNodes, float &totalLength, bool loop);
static void RemoveTemporaryPlanes(void);
static void UpdatePlanes(void);
static bool TestRocketCollision(CVector *rocketPos);
static void CreateIncomingCesna(void);
@ -62,6 +63,8 @@ public:
static bool HasCesnaLanded(void);
static bool HasCesnaBeenDestroyed(void);
static bool HasDropOffCesnaBeenShotDown(void);
static void Load(void);
static void Save(void);
};
extern float LandingPoint;

View File

@ -170,6 +170,7 @@ CExplosion::AddExplosion(CEntity *explodingEntity, CEntity *culprit, eExplosionT
break;
case EXPLOSION_CAR:
case EXPLOSION_CAR_QUICK:
case EXPLOSION_BOAT:
explosion.m_fRadius = 9.0f;
explosion.m_fPower = 300.0f;
explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 4250;
@ -205,6 +206,7 @@ CExplosion::AddExplosion(CEntity *explodingEntity, CEntity *culprit, eExplosionT
}
break;
case EXPLOSION_HELI:
case EXPLOSION_HELI2:
explosion.m_fRadius = 6.0f;
explosion.m_fPower = 300.0f;
explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750;

View File

@ -10,7 +10,9 @@ enum eExplosionType
EXPLOSION_ROCKET,
EXPLOSION_CAR,
EXPLOSION_CAR_QUICK,
EXPLOSION_BOAT,
EXPLOSION_HELI,
EXPLOSION_HELI2,
EXPLOSION_MINE,
EXPLOSION_BARREL,
EXPLOSION_TANK_GRENADE,