Serious-Engine/Sources/Entities/Bullet.es
Daniel Gibson b30d3d86d8 Fix compilation from changes for GCC 6.1.1
At least on my system floor() and log10() return double, so the other
arguments to Clamp() and ClampDn() didn't match anymore, now being float
instead of double.
I replaced the calls to log10f() and floorf() to avoid any ambiguities.
2016-05-30 01:06:10 +02:00

421 lines
14 KiB
C++

502
%{
#include "Entities/StdH/StdH.h"
#define BLOOD_SPILL_RED RGBAToColor(250,20,20,255)
#define BLOOD_SPILL_GREEN RGBAToColor(0,250,0,255)
%}
uses "Entities/BasicEffects";
uses "Engine/Classes/MovableEntity";
// input parameters for bullet
event EBulletInit {
CEntityPointer penOwner, // who launched it
FLOAT fDamage, // damage
};
// hit enum
enum BulletHitType {
0 BHT_NONE "", // none
1 BHT_FLESH "", // flesh
2 BHT_BRUSH_STONE "", // brush stone
3 BHT_BRUSH_SAND "", // brush sand
4 BHT_BRUSH_WATER "", // brush water
5 BHT_BRUSH_UNDER_WATER "", // brush under water
6 BHT_ACID "", // acid
7 BHT_BRUSH_RED_SAND "", // brush red sand
};
%{
void CBullet_OnPrecache(CDLLEntityClass *pdec, INDEX iUser)
{
pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_BULLETSTAINSTONE);
pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_BULLETSTAINSAND);
pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_BULLETSTAINREDSAND);
pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_BULLETSTAINWATER);
pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_BULLETSTAINSTONENOSOUND);
pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_BULLETSTAINSANDNOSOUND);
pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_BULLETSTAINREDSANDNOSOUND);
pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_BULLETSTAINWATERNOSOUND);
pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_BLOODSPILL);
pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_BULLETTRAIL);
}
%}
class export CBullet : CEntity {
name "Bullet";
thumbnail "";
features "ImplementsOnPrecache";
properties:
1 CEntityPointer m_penOwner, // entity which owns it
2 FLOAT m_fDamage = 0.0f, // damage
3 FLOAT3D m_vTarget = FLOAT3D(0,0,0), // bullet target point in space
4 FLOAT3D m_vTargetCopy = FLOAT3D(0,0,0), // copy of bullet target point in space for jitter
6 FLOAT3D m_vHitPoint = FLOAT3D(0,0,0), // hit point
8 INDEX m_iBullet = 0, // bullet for lerped launch
9 enum DamageType m_EdtDamage = DMT_BULLET, // damage type
10 FLOAT m_fBulletSize = 0.0f, // bullet can have radius, for hitting models only
components:
1 class CLASS_BASIC_EFFECT "Classes\\BasicEffect.ecl"
functions:
/************************************************************
* BULLET LAUNCH *
************************************************************/
// set bullet damage
void SetDamage(FLOAT fDamage) {
m_fDamage = fDamage;
};
// calc jitter target
void CalcTarget(FLOAT fRange) {
// destination in bullet direction
AnglesToDirectionVector(GetPlacement().pl_OrientationAngle, m_vTarget);
m_vTarget *= fRange;
m_vTarget += GetPlacement().pl_PositionVector;
m_vTargetCopy = m_vTarget;
};
void CalcTarget(CEntity *pen, FLOAT fRange) {
FLOAT3D vTarget;
// target body
EntityInfo *peiTarget = (EntityInfo*) (pen->GetEntityInfo());
GetEntityInfoPosition(pen, peiTarget->vTargetCenter, vTarget);
// calculate
m_vTarget = (vTarget - GetPlacement().pl_PositionVector).Normalize();
m_vTarget *= fRange;
m_vTarget += GetPlacement().pl_PositionVector;
m_vTargetCopy = m_vTarget;
};
// calc jitter target - !!! must call CalcTarget first !!!
void CalcJitterTarget(FLOAT fR) {
FLOAT3D vJitter;
/* My Sphere
FLOAT fXZ = FRnd()*360.0f;
FLOAT fXY = FRnd()*360.0f;
// sphere
fR *= FRnd();
vJitter(1) = CosFast(fXZ)*CosFast(fXY)*fR;
vJitter(2) = CosFast(fXZ)*SinFast(fXY)*fR;
vJitter(3) = SinFast(fXZ)*fR;*/
// comp graphics algorithms sphere
FLOAT fZ = FRnd()*2.0f - 1.0f;
FLOAT fA = FRnd()*360.0f;
FLOAT fT = Sqrt(1-(fZ*fZ));
vJitter(1) = fT * CosFast(fA);
vJitter(2) = fT * SinFast(fA);
vJitter(3) = fZ;
vJitter = vJitter*fR*FRnd();
// target
m_vTarget = m_vTargetCopy + vJitter;
};
// calc jitter target asymetric - !!! must call CalcTarget first !!!
void CalcJitterTargetFixed(FLOAT fX, FLOAT fY, FLOAT fJitter) {
FLOAT fRndX = FRnd()*2.0f - 1.0f;
FLOAT fRndY = FRnd()*2.0f - 1.0f;
FLOAT3D vX, vY;
const FLOATmatrix3D &m=GetRotationMatrix();
vX(1) = m(1,1); vX(2) = m(2,1); vX(3) = m(3,1);
vY(1) = m(1,2); vY(2) = m(2,2); vY(3) = m(3,2);
// target
m_vTarget = m_vTargetCopy + (vX*(fX+fRndX*fJitter)) + (vY*(fY+fRndY*fJitter));
};
// launch one bullet
void LaunchBullet(BOOL bSound, BOOL bTrail, BOOL bHitFX)
{
// cast a ray to find bullet target
CCastRay crRay( m_penOwner, GetPlacement().pl_PositionVector, m_vTarget);
crRay.cr_bHitPortals = TRUE;
crRay.cr_bHitTranslucentPortals = TRUE;
crRay.cr_ttHitModels = CCastRay::TT_COLLISIONBOX;
crRay.cr_bPhysical = FALSE;
crRay.cr_fTestR = m_fBulletSize;
FLOAT3D vHitDirection;
AnglesToDirectionVector(GetPlacement().pl_OrientationAngle, vHitDirection);
INDEX ctCasts = 0;
while( ctCasts<10)
{
if(ctCasts == 0)
{
// perform first cast
GetWorld()->CastRay(crRay);
}
else
{
// next casts
GetWorld()->ContinueCast(crRay);
}
ctCasts++;
// stop casting if nothing hit
if (crRay.cr_penHit==NULL)
{
break;
}
// apply damage
InflictDirectDamage(crRay.cr_penHit, m_penOwner, m_EdtDamage, m_fDamage,
crRay.cr_vHit, vHitDirection);
m_vHitPoint = crRay.cr_vHit;
// if brush hitted
if (crRay.cr_penHit->GetRenderType()==RT_BRUSH && crRay.cr_pbpoBrushPolygon!=NULL)
{
CBrushPolygon *pbpo = crRay.cr_pbpoBrushPolygon;
FLOAT3D vHitNormal = FLOAT3D(pbpo->bpo_pbplPlane->bpl_plAbsolute);
// obtain surface type
INDEX iSurfaceType = pbpo->bpo_bppProperties.bpp_ubSurfaceType;
BulletHitType bhtType = BHT_BRUSH_STONE;
// get content type
INDEX iContent = pbpo->bpo_pbscSector->GetContentType();
CContentType &ct = GetWorld()->wo_actContentTypes[iContent];
// if this is under water polygon
if( ct.ct_ulFlags&CTF_BREATHABLE_GILLS)
{
// if we hit water surface
if( iSurfaceType==SURFACE_WATER)
{
vHitNormal = -vHitNormal;
bhtType=BHT_BRUSH_WATER;
}
// if we hit stone under water
else
{
bhtType=BHT_BRUSH_UNDER_WATER;
}
}
else
{
if( iSurfaceType==SURFACE_SAND) {bhtType=BHT_BRUSH_SAND;}
if( iSurfaceType==SURFACE_RED_SAND) {bhtType=BHT_BRUSH_RED_SAND;}
if( iSurfaceType==SURFACE_WATER) {bhtType=BHT_BRUSH_WATER;}
}
// spawn hit effect
BOOL bPassable = pbpo->bpo_ulFlags & (BPOF_PASSABLE|BPOF_SHOOTTHRU);
if (!bPassable || iSurfaceType==SURFACE_WATER) {
SpawnHitTypeEffect(bhtType, bSound, vHitNormal, crRay.cr_vHit, vHitDirection, FLOAT3D(0.0f, 0.0f, 0.0f));
}
if(!bPassable) {
break;
}
// if not brush
} else {
// if flesh entity
if (crRay.cr_penHit->GetEntityInfo()!=NULL) {
if( ((EntityInfo*)crRay.cr_penHit->GetEntityInfo())->Eeibt == EIBT_FLESH)
{
CEntity *penOfFlesh = crRay.cr_penHit;
FLOAT3D vHitNormal = (GetPlacement().pl_PositionVector-m_vTarget).Normalize();
FLOAT3D vOldHitPos = crRay.cr_vHit;
FLOAT3D vDistance;
// look behind the entity (for back-stains)
GetWorld()->ContinueCast(crRay);
if( crRay.cr_penHit!=NULL && crRay.cr_pbpoBrushPolygon!=NULL &&
crRay.cr_penHit->GetRenderType()==RT_BRUSH)
{
vDistance = crRay.cr_vHit-vOldHitPos;
vHitNormal = FLOAT3D(crRay.cr_pbpoBrushPolygon->bpo_pbplPlane->bpl_plAbsolute);
}
else
{
vDistance = FLOAT3D(0.0f, 0.0f, 0.0f);
vHitNormal = FLOAT3D(0,0,0);
}
if(IsOfClass(penOfFlesh, "Gizmo") ||
IsOfClass(penOfFlesh, "Beast"))
{
// spawn green blood hit spill effect
SpawnHitTypeEffect(BHT_ACID, bSound, vHitNormal, crRay.cr_vHit, vHitDirection, vDistance);
}
else
{
// spawn red blood hit spill effect
SpawnHitTypeEffect(BHT_FLESH, bSound, vHitNormal, crRay.cr_vHit, vHitDirection, vDistance);
}
break;
}
}
// stop casting ray if not brush
break;
}
}
if( bTrail)
{
SpawnTrail();
}
};
// destroy yourself
void DestroyBullet(void) {
Destroy();
};
/************************************************************
* EFFECTS *
************************************************************/
// spawn effect from hit type
void SpawnHitTypeEffect(enum BulletHitType bhtType, BOOL bSound, FLOAT3D vHitNormal, FLOAT3D vHitPoint,
FLOAT3D vDirection, FLOAT3D vDistance)
{
switch (bhtType)
{
case BHT_BRUSH_STONE:
case BHT_BRUSH_SAND:
case BHT_BRUSH_RED_SAND:
case BHT_BRUSH_WATER:
case BHT_BRUSH_UNDER_WATER:
{
// bullet stain
ESpawnEffect ese;
if( bSound)
{
if( bhtType == BHT_BRUSH_STONE) {ese.betType = BET_BULLETSTAINSTONE;};
if( bhtType == BHT_BRUSH_SAND) {ese.betType = BET_BULLETSTAINSAND;};
if( bhtType == BHT_BRUSH_RED_SAND) {ese.betType = BET_BULLETSTAINREDSAND;};
if( bhtType == BHT_BRUSH_WATER) {ese.betType = BET_BULLETSTAINWATER;};
if( bhtType == BHT_BRUSH_UNDER_WATER) {ese.betType = BET_BULLETSTAINUNDERWATER;};
}
else
{
if( bhtType == BHT_BRUSH_STONE) {ese.betType = BET_BULLETSTAINSTONENOSOUND;};
if( bhtType == BHT_BRUSH_SAND) {ese.betType = BET_BULLETSTAINSANDNOSOUND;};
if( bhtType == BHT_BRUSH_RED_SAND) {ese.betType = BET_BULLETSTAINREDSANDNOSOUND;};
if( bhtType == BHT_BRUSH_WATER) {ese.betType = BET_BULLETSTAINWATERNOSOUND;};
if( bhtType == BHT_BRUSH_UNDER_WATER) {ese.betType = BET_BULLETSTAINUNDERWATERNOSOUND;};
}
ese.vNormal = vHitNormal;
ese.colMuliplier = C_WHITE|CT_OPAQUE;
FLOAT3D vDirection = (vHitPoint-GetPlacement().pl_PositionVector).Normalize();
// reflect direction arround normal
FLOAT fNx = vHitNormal(1);
FLOAT fNy = vHitNormal(2);
FLOAT fNz = vHitNormal(3);
FLOAT fNV = fNx*vDirection(1) + fNy*vDirection(2) + fNz*vDirection(3);
FLOAT fRVx = vDirection(1) - 2*fNx*fNV;
FLOAT fRVy = vDirection(2) - 2*fNy*fNV;
FLOAT fRVz = vDirection(3) - 2*fNz*fNV;
ese.vStretch = FLOAT3D( fRVx, fRVy, fRVz);
SpawnEffect(vHitPoint, ese);
break;
}
case BHT_FLESH:
case BHT_ACID:
{
// spawn bullet entry wound
ESpawnEffect ese;
ese.colMuliplier = C_WHITE|CT_OPAQUE;
// if there is exit wound blood spill place
FLOAT fDistance = vDistance.Length();
if( fDistance>0.01f && !(IRnd()%2) )
{
// spawn bullet exit wound blood patch
ese.betType = BET_BLOODSPILL;
if( bhtType == BHT_ACID)
{
ese.colMuliplier = BLOOD_SPILL_GREEN;
}
else
{
ese.colMuliplier = BLOOD_SPILL_RED;
}
ese.vNormal = vHitNormal;
if (fDistance<25.0f)
{
GetNormalComponent( vDistance/fDistance, vHitNormal, ese.vDirection);
FLOAT fLength = ese.vDirection.Length();
fLength = Clamp( fLength*3, 1.0f, 3.0f);
fDistance = Clamp( log10f(fDistance), 0.5f, 2.0f);
ese.vStretch = FLOAT3D( fDistance, fLength*fDistance, 1.0f);
SpawnEffect(vHitPoint, ese);
}
}
break;
}
}
};
// spawn trail of this bullet
void SpawnTrail(void)
{
// get bullet path positions
const FLOAT3D &v0 = GetPlacement().pl_PositionVector;
const FLOAT3D &v1 = m_vHitPoint;
// calculate distance
FLOAT3D vD = v1-v0;
FLOAT fD = vD.Length();
// if too short
if (fD<1.0f) {
// no trail
return;
}
// length must be such that it doesn't get out of path
FLOAT fLen = Min(20.0f, fD);
// position is random, but it must not make trail go out of path
FLOAT3D vPos;
if (fLen<fD) {
vPos = Lerp(v0, v1, FRnd()*(fD-fLen)/fD);
} else {
vPos = v0;
}
ESpawnEffect ese;
UBYTE ubRndH = UBYTE( 8+FRnd()*32);
UBYTE ubRndS = UBYTE( 8+FRnd()*32);
UBYTE ubRndV = UBYTE( 224+FRnd()*32);
UBYTE ubRndA = UBYTE( 32+FRnd()*128);
ese.colMuliplier = HSVToColor(ubRndH, ubRndS, ubRndV)|ubRndA;
ese.betType = BET_BULLETTRAIL;
ese.vNormal = vD/fD;
ese.vStretch = FLOAT3D(0.1f, fLen, 1.0f);
SpawnEffect(vPos , ese);
}
// spawn effect
void SpawnEffect(const FLOAT3D &vHit, const ESpawnEffect &eSpawnEffect)
{
FLOAT3D vDirection;
vDirection = (m_vTarget-GetPlacement().pl_PositionVector).Normalize();
CPlacement3D plHit = CPlacement3D(vHit-vDirection*0.1f, GetPlacement().pl_OrientationAngle);
CEntityPointer penHit = CreateEntity(plHit , CLASS_BASIC_EFFECT);
penHit->Initialize(eSpawnEffect);
};
procedures:
Main(EBulletInit eInit)
{
// remember the initial parameters
ASSERT(eInit.penOwner!=NULL);
m_penOwner = eInit.penOwner;
m_fDamage = eInit.fDamage;
InitAsVoid();
SetPhysicsFlags(EPF_MODEL_IMMATERIAL);
SetCollisionFlags(ECF_IMMATERIAL);
// for lerped launch
m_iBullet = 0;
return;
};
};