Serious-Engine/Sources/EntitiesMP/Flame.es
2016-04-07 18:13:50 +02:00

385 lines
12 KiB
C++
Executable File

/* Copyright (c) 2002-2012 Croteam Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
This program 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 this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
504
%{
#include "EntitiesMP/StdH/StdH.h"
#define TM_APPLY_DAMAGE_QUANTUM 0.25f
#define TM_APPLY_WHOLE_DAMAGE 7.5f
#define DAMAGE_AMMOUNT 30.0f
#define MIN_DAMAGE_QUANTUM (DAMAGE_AMMOUNT/TM_APPLY_WHOLE_DAMAGE*TM_APPLY_DAMAGE_QUANTUM)
#define MAX_DAMAGE_QUANTUM (MIN_DAMAGE_QUANTUM*10.0f)
#define DEATH_BURN_TIME 4.0f
#include "EntitiesMP/MovingBrush.h"
%}
uses "EntitiesMP/Light";
// input parameter for flame
event EFlame {
CEntityPointer penOwner, // entity which owns it
CEntityPointer penAttach, // entity on which flame is attached (his parent)
};
// event for stop burning
event EStopFlaming {
BOOL m_bNow,
};
%{
void CFlame_OnPrecache(CDLLEntityClass *pdec, INDEX iUser)
{
pdec->PrecacheModel(MODEL_FLAME);
pdec->PrecacheTexture(TEXTURE_FLAME);
pdec->PrecacheSound(SOUND_FLAME);
}
%}
class CFlame : CMovableModelEntity {
name "Flame";
thumbnail "";
features "ImplementsOnPrecache", "CanBePredictable";
properties:
1 CEntityPointer m_penOwner, // entity which owns it
2 CEntityPointer m_penAttach, // entity on which flame is attached (his parent)
5 BOOL m_bLoop = FALSE, // internal for loops
8 FLOAT3D m_vHitPoint = FLOAT3D(0.0f, 0.0f, 0.0f), // where the flame hit the entity
10 CSoundObject m_soEffect, // sound channel
20 FLOAT m_tmStart = 0.0f,
21 FLOAT m_fDamageToApply = 0.0f,
22 FLOAT m_fDamageStep = 0.0f,
23 FLOAT m_fAppliedDamage=0.0f,
24 FLOAT m_tmFirstStart = 0.0f, // when the burning started
29 INDEX m_ctFlames=0,
30 FLOAT3D m_vPos01=FLOAT3D(0,0,0),
31 FLOAT3D m_vPos02=FLOAT3D(0,0,0),
32 FLOAT3D m_vPos03=FLOAT3D(0,0,0),
33 FLOAT3D m_vPos04=FLOAT3D(0,0,0),
34 FLOAT3D m_vPos05=FLOAT3D(0,0,0),
35 FLOAT3D m_vPos06=FLOAT3D(0,0,0),
36 FLOAT3D m_vPos07=FLOAT3D(0,0,0),
37 FLOAT3D m_vPos08=FLOAT3D(0,0,0),
38 FLOAT3D m_vPos09=FLOAT3D(0,0,0),
39 FLOAT3D m_vPos10=FLOAT3D(0,0,0),
40 FLOAT3D m_vPlaneNormal=FLOAT3D(0,0,0),
51 BOOL m_bBurningBrush=FALSE,
52 FLOAT m_tmDeathParticlesStart=1e6,
{
CLightSource m_lsLightSource;
}
components:
1 class CLASS_LIGHT "Classes\\Light.ecl",
// ********* FLAME *********
10 model MODEL_FLAME "ModelsMP\\Effects\\Flame\\Flame.mdl",
11 texture TEXTURE_FLAME "ModelsMP\\Effects\\Flame\\Flame.tex",
12 sound SOUND_FLAME "SoundsMP\\Fire\\Burning.wav",
functions:
// add to prediction any entities that this entity depends on
void AddDependentsToPrediction(void)
{
m_penOwner->AddToPrediction();
}
// postmoving
void PostMoving(void) {
CMovableModelEntity::PostMoving();
// if no air
CContentType &ctDn = GetWorld()->wo_actContentTypes[en_iDnContent];
// stop existing
if (!(ctDn.ct_ulFlags&CTF_BREATHABLE_LUNGS)) {
EStopFlaming esf;
esf.m_bNow = TRUE;
SendEvent(esf);
}
// never remove from list of movers
en_ulFlags &= ~ENF_INRENDERING;
// not moving in fact, only moving with its parent
en_plLastPlacement = en_plPlacement;
};
/* Read from stream. */
void Read_t( CTStream *istr) // throw char *
{
CMovableModelEntity::Read_t(istr);
SetupLightSource();
}
BOOL IsPointInsidePolygon(const FLOAT3D &vPos, CBrushPolygon *pbpo)
{
FLOATplane3D &plPlane=pbpo->bpo_pbplPlane->bpl_plAbsolute;
// find major axes of the polygon plane
INDEX iMajorAxis1, iMajorAxis2;
GetMajorAxesForPlane(plPlane, iMajorAxis1, iMajorAxis2);
// create an intersector
CIntersector isIntersector(vPos(iMajorAxis1), vPos(iMajorAxis2));
// for all edges in the polygon
FOREACHINSTATICARRAY(pbpo->bpo_abpePolygonEdges, CBrushPolygonEdge, itbpePolygonEdge) {
// get edge vertices (edge direction is irrelevant here!)
const FLOAT3D &vVertex0 = itbpePolygonEdge->bpe_pbedEdge->bed_pbvxVertex0->bvx_vAbsolute;
const FLOAT3D &vVertex1 = itbpePolygonEdge->bpe_pbedEdge->bed_pbvxVertex1->bvx_vAbsolute;
// pass the edge to the intersector
isIntersector.AddEdge(
vVertex0(iMajorAxis1), vVertex0(iMajorAxis2),
vVertex1(iMajorAxis1), vVertex1(iMajorAxis2));
}
// return result of polygon intersection
return isIntersector.IsIntersecting();
}
/* Get static light source information. */
CLightSource *GetLightSource(void)
{
if (!IsPredictor()) {
return &m_lsLightSource;
} else {
return NULL;
}
}
// render particles
void RenderParticles(void)
{
FLOAT fTimeFactor=CalculateRatio(_pTimer->CurrentTick(), m_tmFirstStart, m_tmStart+TM_APPLY_WHOLE_DAMAGE, 0.05f, 0.2f);
FLOAT fDeathFactor=1.0f;
if( _pTimer->CurrentTick()>m_tmDeathParticlesStart)
{
fDeathFactor=1.0f-Clamp((_pTimer->CurrentTick()-m_tmDeathParticlesStart)/DEATH_BURN_TIME, 0.0f, 1.0f);
}
CEntity *penParent= GetParent();
FLOAT fPower=ClampUp(m_fDamageStep-MIN_DAMAGE_QUANTUM, MAX_DAMAGE_QUANTUM)/MAX_DAMAGE_QUANTUM;
if( penParent!= NULL)
{
if( (penParent->en_RenderType==CEntity::RT_MODEL || penParent->en_RenderType==CEntity::RT_EDITORMODEL ||
penParent->en_RenderType==CEntity::RT_SKAMODEL || penParent->en_RenderType==CEntity::RT_SKAEDITORMODEL) &&
(Particle_GetViewer()!=penParent) )
{
Particles_Burning(penParent, fPower, fTimeFactor*fDeathFactor);
}
else
{
Particles_BrushBurning(this, &m_vPos01, m_ctFlames, m_vPlaneNormal, fPower, fTimeFactor*fDeathFactor);
}
}
}
// Setup light source
void SetupLightSource(void)
{
// setup light source
CLightSource lsNew;
lsNew.ls_ulFlags = LSF_NONPERSISTENT|LSF_DYNAMIC;
if(m_bBurningBrush)
{
UBYTE ubRndH = UBYTE( 25+(FLOAT(rand())/RAND_MAX-0.5f)*28);
UBYTE ubRndS = 166;
UBYTE ubRndV = 48;
lsNew.ls_colColor = HSVToColor(ubRndH, ubRndS, ubRndV);
//lsNew.ls_colColor = 0x3F3F1600;
lsNew.ls_rFallOff = 4.0f;
lsNew.ls_rHotSpot = 0.2f;
}
else
{
lsNew.ls_colColor = 0x8F8F5000;
lsNew.ls_rFallOff = 6.0f;
lsNew.ls_rHotSpot = 0.50f;
}
lsNew.ls_plftLensFlare = NULL;
lsNew.ls_ubPolygonalMask = 0;
lsNew.ls_paoLightAnimation = NULL;
m_lsLightSource.ls_penEntity = this;
m_lsLightSource.SetLightSource(lsNew);
}
/************************************************************
* P R O C E D U R E S *
************************************************************/
procedures:
// --->>> MAIN
Main(EFlame ef) {
// attach to parent (another entity)
ASSERT(ef.penOwner!=NULL);
ASSERT(ef.penAttach!=NULL);
m_penOwner = ef.penOwner;
m_penAttach = ef.penAttach;
m_tmStart = _pTimer->CurrentTick();
m_tmFirstStart=m_tmStart;
SetParent(ef.penAttach);
// initialization
InitAsEditorModel();
SetPhysicsFlags(EPF_MODEL_FLYING);
SetCollisionFlags(ECF_FLAME);
SetFlags(GetFlags() | ENF_SEETHROUGH);
SetModel(MODEL_FLAME);
SetModelMainTexture(TEXTURE_FLAME);
ModelChangeNotify();
// play the burning sound
m_soEffect.Set3DParameters(10.0f, 1.0f, 1.0f, 1.0f);
PlaySound(m_soEffect, SOUND_FLAME, SOF_3D|SOF_LOOP);
// must always be in movers, to find sector content type
AddToMovers();
m_bBurningBrush=FALSE;
BOOL bAllowFlame=TRUE;
if( !(ef.penAttach->en_RenderType==CEntity::RT_MODEL || ef.penAttach->en_RenderType==CEntity::RT_EDITORMODEL ||
ef.penAttach->en_RenderType==CEntity::RT_SKAMODEL || ef.penAttach->en_RenderType==CEntity::RT_SKAEDITORMODEL ))
{
m_bBurningBrush=TRUE;
FLOAT3D vPos=GetPlacement().pl_PositionVector;
FLOATplane3D plPlane;
FLOAT fDistanceToEdge;
FindSectorsAroundEntity();
CBrushPolygon *pbpo=NULL;
pbpo=GetNearestPolygon(vPos, plPlane, fDistanceToEdge);
FLOAT3D vBrushPos = ef.penAttach->GetPlacement().pl_PositionVector;
FLOATmatrix3D mBrushRotInv = !ef.penAttach->GetRotationMatrix();
if( pbpo!=NULL && pbpo->bpo_pbscSector->bsc_pbmBrushMip->bm_pbrBrush->br_penEntity==ef.penAttach)
{
plPlane = pbpo->bpo_pbplPlane->bpl_plAbsolute;
m_vPlaneNormal=(FLOAT3D &)plPlane;
m_vPlaneNormal.Normalize();
// ------ Calculate plane-paralel normal vectors
FLOAT3D vU, vV;
//CPrintF("plPlane %g\n", plPlane(2));
if(plPlane(2)<-0.1f)
{
bAllowFlame=FALSE;
}
// if the plane is mostly horizontal
if (Abs(plPlane(2))>0.5f) {
// use cross product of +x axis and plane normal as +s axis
vU = FLOAT3D(1.0f, 0.0f, 0.0f)*m_vPlaneNormal;
// if the plane is mostly vertical
} else {
// use cross product of +y axis and plane normal as +s axis
vU = FLOAT3D(0.0f, 1.0f, 0.0f)*m_vPlaneNormal;
}
// make +s axis normalized
vU.Normalize();
// use cross product of plane normal and +s axis as +t axis
vV = vU*m_vPlaneNormal;
vV.Normalize();
// counter of valid flames
m_ctFlames=0;
for(INDEX iTest=0; iTest<20; iTest++)
{
FLOAT fA=FRnd()*360.0f;
FLOAT fR=FRnd()*2.0f;
FLOAT3D vRndV=vV*fR*SinFast(fA);
FLOAT3D vRndU=vU*fR*CosFast(fA);
FLOAT3D vRndPos=vPos;
if( iTest!=0)
{
vRndPos+=vRndV+vRndU;
}
// project point to a plane
FLOAT3D vProjectedRndPos=plPlane.ProjectPoint(vRndPos);
if( IsPointInsidePolygon(vProjectedRndPos, pbpo))
{
(&m_vPos01)[ m_ctFlames]=(vProjectedRndPos-vBrushPos)*mBrushRotInv;
m_ctFlames++;
if( m_ctFlames==6) { break; };
}
}
}
else
{
bAllowFlame=FALSE;
}
}
// setup light source
if( bAllowFlame)
{
SetupLightSource();
}
m_bLoop = bAllowFlame;
while(m_bLoop) {
wait(TM_APPLY_DAMAGE_QUANTUM) {
// damage to parent
on (EBegin) : {
// if parent does not exist anymore
if (m_penAttach==NULL || (m_penAttach->GetFlags()&ENF_DELETED)) {
// stop existing
m_bLoop = FALSE;
stop;
}
// inflict damage to parent
const FLOAT fDamageMul = GetSeriousDamageMultiplier(m_penOwner);
FLOAT fDamageToApply=fDamageMul*(m_fDamageToApply/TM_APPLY_WHOLE_DAMAGE*TM_APPLY_DAMAGE_QUANTUM)*m_fDamageStep;
m_penAttach->InflictDirectDamage( m_penAttach, m_penOwner, DMT_BURNING, fDamageToApply,
GetPlacement().pl_PositionVector, -en_vGravityDir);
m_fAppliedDamage+=fDamageToApply;
resume;
}
on (EFlame ef) : {
m_penOwner = ef.penOwner;
FLOAT fTimeLeft=m_tmStart+TM_APPLY_WHOLE_DAMAGE-_pTimer->CurrentTick();
FLOAT fDamageLeft=(fTimeLeft/TM_APPLY_DAMAGE_QUANTUM)*m_fDamageStep;
m_fDamageToApply=ClampUp(fDamageLeft+DAMAGE_AMMOUNT, 80.0f);
m_tmStart=_pTimer->CurrentTick();
m_fDamageStep=m_fDamageToApply/(TM_APPLY_WHOLE_DAMAGE/TM_APPLY_DAMAGE_QUANTUM);
resume;
};
on (EStopFlaming esf) : {
if( !esf.m_bNow)
{
m_tmDeathParticlesStart=_pTimer->CurrentTick();
resume;
}
else
{
m_bLoop = FALSE;
stop;
}
};
on (EBrushDestroyed) : {
m_bLoop = FALSE;
stop;
};
on (ETimer) : { stop; }
}
if(_pTimer->CurrentTick()>m_tmStart+TM_APPLY_WHOLE_DAMAGE)
{
m_bLoop = FALSE;
}
}
// cease to exist
Destroy();
return;
}
};