607 %{ #include "Entities/StdH/StdH.h" #include "Entities/BackgroundViewer.h" #include "Entities/WorldSettingsController.h" #include "Entities/Light.h" %} %{ struct ThunderInfo { INDEX ti_iSound; FLOAT ti_fThunderStrikeDelay; }; struct ThunderInfo _atiThunderSounds[3] = { { SOUND_THUNDER1, 0.6f}, { SOUND_THUNDER2, 0.0f}, { SOUND_THUNDER3, 0.0f}, }; %} class CLightning: CMovableModelEntity { name "Lightning"; thumbnail "Thumbnails\\Lightning.tbn"; features "IsTargetable", "HasName"; properties: 1 CEntityPointer m_penTarget "Target" 'T' COLOR(C_BLUE|0xFF), // ptr to lightninig target 2 CEntityPointer m_penwsc, // ptr to world settings controller 3 CTString m_strName "Name" 'N' = "Lightning", // class name 4 FLOAT m_tmLightningStart = -1.0f, // lightning start time 5 CSoundObject m_soThunder, // sound channel 6 BOOL m_bBackground "Background" 'B' =FALSE, 7 CEntityPointer m_penLight "Light" 'L' COLOR(C_CYAN|0xFF), // ptr to light 8 ANIMATION m_iLightAnim "Light Animation" 'A' = 0, 9 INDEX m_iSoundPlaying = 0, 10 FLOAT m_fLightningPower "Lightning power" 'P' = 1.0f, // lightning's ligting power 11 FLOAT m_fSoundDelay "Sound delay" 'D' = 0.0f, // thunder's delay components: 1 model MODEL_TELEPORT "Models\\Editor\\Teleport.mdl", 2 texture TEXTURE_TELEPORT "Models\\Editor\\BoundingBox.tex", 3 sound SOUND_THUNDER1 "Sounds\\Environment\\Thunders\\Thunder1.wav", 4 sound SOUND_THUNDER2 "Sounds\\Environment\\Thunders\\Thunder2.wav", 5 sound SOUND_THUNDER3 "Sounds\\Environment\\Thunders\\Thunder3.wav", functions: void Precache(void) { CMovableModelEntity::Precache(); PrecacheSound(SOUND_THUNDER1); PrecacheSound(SOUND_THUNDER2); PrecacheSound(SOUND_THUNDER3); } /* Get anim data for given animation property - return NULL for none. */ CAnimData *GetAnimData(SLONG slPropertyOffset) { if (m_penLight==NULL) { return NULL; } // if light entity if (IsOfClass(m_penLight, "Light")) { CLight *penLight = (CLight*)&*m_penLight; if (slPropertyOffset==offsetof(CLightning, m_iLightAnim)) { return penLight->m_aoLightAnimation.GetData(); } } else { WarningMessage("Target '%s' is not of light class!", (const char*)m_penLight->GetName()); } return NULL; }; void RenderParticles(void) { if( m_penTarget==NULL) {return;}; TIME tmNow = _pTimer->GetLerpedCurrentTick(); // if lightning is traveling if( ((tmNow-m_tmLightningStart) > 0.0f) && ((tmNow-m_tmLightningStart) < 1.5f) ) { // render lightning particles FLOAT3D vSrc = GetPlacement().pl_PositionVector; FLOAT3D vDst = m_penTarget->GetPlacement().pl_PositionVector; Particles_Lightning( vSrc, vDst, m_tmLightningStart); } } procedures: LightningStike() { // choose random sound m_iSoundPlaying = 1+IRnd()%( ARRAYCOUNT(_atiThunderSounds)-1); if( m_fSoundDelay != 0) { m_iSoundPlaying=0; } m_soThunder.SetVolume(1.5f*m_fLightningPower, 1.5f*m_fLightningPower); m_soThunder.SetPitch(Lerp(0.9f, 1.2f, FRnd())); if( m_fSoundDelay == 0.0f) { // play thunder ! PlaySound(m_soThunder, _atiThunderSounds[ m_iSoundPlaying].ti_iSound, 0); } // wait for sound to progress to lightning strike autowait( _atiThunderSounds[ m_iSoundPlaying].ti_fThunderStrikeDelay); // remember current time as lightning start time TIME tmNow = _pTimer->CurrentTick(); m_tmLightningStart = tmNow; // also in world settings controller ((CWorldSettingsController *)&*m_penwsc)->m_tmLightningStart = tmNow; // set power of lightning ((CWorldSettingsController *)&*m_penwsc)->m_fLightningPower = m_fLightningPower; // trigger light animation if( m_penLight != NULL) { EChangeAnim eChange; eChange.iLightAnim = m_iLightAnim; eChange.bLightLoop = FALSE; m_penLight->SendEvent(eChange); } if( m_fSoundDelay != 0.0f) { // wait given delay time autowait( m_fSoundDelay); // play thunder ! PlaySound(m_soThunder, _atiThunderSounds[ m_iSoundPlaying].ti_iSound, 0); } // wait until end of sound wait( GetSoundLength(_atiThunderSounds[ m_iSoundPlaying].ti_iSound)- _atiThunderSounds[ m_iSoundPlaying].ti_fThunderStrikeDelay) { on (ETimer) : { stop; } otherwise() : { resume; }; } return EBegin(); } Main(EVoid) { // set appearance InitAsEditorModel(); SetPhysicsFlags(EPF_MODEL_IMMATERIAL); SetCollisionFlags(ECF_IMMATERIAL); // set appearance SetModel(MODEL_TELEPORT); SetModelMainTexture(TEXTURE_TELEPORT); // see if it is lightning on background if (m_bBackground) { SetFlags(GetFlags()|ENF_BACKGROUND); } else { SetFlags(GetFlags()&~ENF_BACKGROUND); } // obtain bcg viewer entity CBackgroundViewer *penBcgViewer = (CBackgroundViewer *) GetWorld()->GetBackgroundViewer(); if( penBcgViewer == NULL) { // don't do anything return; } // obtain world settings controller m_penwsc = penBcgViewer->m_penWorldSettingsController; if( m_penwsc == NULL) { // don't do anything return; } // must be world settings controller entity if (!IsOfClass(m_penwsc, "WorldSettingsController")) { // don't do anything return; } // lightning target must be marker if( (m_penTarget == NULL) || (!IsOfClass(m_penTarget, "Marker")) ) { if( m_penTarget != NULL) { WarningMessage("Target '%s' is not of Marker class!", (const char*)m_penTarget->GetName()); } // don't do anything return; } // stretch model FLOAT3D vDirection = (m_penTarget->GetPlacement().pl_PositionVector- GetPlacement().pl_PositionVector); FLOAT3D vStretch = vDirection; vStretch(1) = 1.0f; vStretch(2) = 1.0f; vStretch(3) = -vDirection.Length(); // set entity orientation CPlacement3D pl = GetPlacement(); DirectionVectorToAngles(vDirection.Normalize(), pl.pl_OrientationAngle); SetPlacement(pl); GetModelObject()->StretchModel(vStretch); ModelChangeNotify(); // correct power factor to fall under 0-1 boundaries m_fLightningPower = Clamp( m_fLightningPower, 0.0f, 1.0f); // spawn in world editor autowait(0.1f); while (TRUE) { wait() { on (EBegin) : { resume; } on (ETrigger eTrigger) : { call LightningStike(); resume; } otherwise() : { resume; }; }; } } };