mirror of
https://github.com/ptitSeb/Serious-Engine
synced 2024-12-25 07:14:50 +01:00
385 lines
12 KiB
C++
385 lines
12 KiB
C++
/* 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 "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.5) {
|
|
// 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;
|
|
}
|
|
};
|