lightning.cpp

Engine/source/T3D/fx/lightning.cpp

More...

Classes:

Public Variables

Public Functions

ConsoleDocClass(Lightning , "@brief An emitter <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> lightning <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bolts.\n\n</a>" "<a href="/coding/class/classlightning/">Lightning</a> strike events are created on the server and transmitted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> all " "clients <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> the bolt. The strike may be followed by <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> random thunder " "sound. <a href="/coding/class/classplayer/">Player</a> or <a href="/coding/class/classvehicle/">Vehicle</a> objects within the <a href="/coding/class/classlightning/">Lightning</a> strike range can be " "hit and damaged by <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bolts.\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">LightningData\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Atmosphere\n</a>" )
ConsoleDocClass(LightningData , "@brief Common data <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classlightning/">Lightning</a> emitter <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Lightning\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Atmosphere\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )
ConsoleDocClass(LightningStrikeEvent , "@brief Network event that triggers <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> lightning strike on the client when it " "is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">received.\n\n</a>" "This event is sent <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> all clients when the  warningFlashes, " "strikeRandomPoint() or strikeObject() methods are invoked on the <a href="/coding/class/classlightning/">Lightning</a> " "object on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">server.\n</a>" " @see Lightning, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">LightningData\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Atmosphere\n</a>" )
DefineEngineMethod(Lightning , strikeObject , void , (ShapeBase *pSB) , (nullAsType< ShapeBase * >()) , "Creates <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classlightningstrikeevent/">LightningStrikeEvent</a> which strikes <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> specific <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@note This method is currently <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">unimplemented.\n</a>" )
DefineEngineMethod(Lightning , strikeRandomPoint , void , () , "Creates <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classlightningstrikeevent/">LightningStrikeEvent</a> which attempts <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> strike and damage <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> random " "object in range of the <a href="/coding/class/classlightning/">Lightning</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Generate <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> damaging lightning strike effect on all <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">clients\n</a>" "%lightning.strikeRandomPoint();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@endtsexample" )
DefineEngineMethod(Lightning , warningFlashes , void , () , "@brief Creates <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classlightningstrikeevent/">LightningStrikeEvent</a> that triggers harmless lightning " "bolts on all <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">clients.\n</a>" "No objects will be damaged by these <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bolts.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Generate <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> harmless lightning strike effect on all <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">clients\n</a>" "%lightning.warningFlashes();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@endtsexample" )
IMPLEMENT_CALLBACK(Lightning , applyDamage , void , (const Point3F &hitPosition, const Point3F &hitNormal, SceneObject *hitObject) , (hitPosition, hitNormal, hitObject) , "Informs an object that it was hit by <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> lightning bolt and needs <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> take <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">damage.\n</a>" "@param hitPosition <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a81f4537631c9ab219ec74de554483adc">World</a> position hit by the lightning <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bolt.\n</a>" "@param hitNormal Surface normal at @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">hitPosition.\n</a>" "@param hitObject <a href="/coding/class/classplayer/">Player</a> or <a href="/coding/class/classvehicle/">Vehicle</a> object that was <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">hit.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "function Lightning::applyDamage( %this, %hitPosition, %hitNormal, %hitObject )\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   // apply damage <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">player\n</a>" "   %hitObject.applyDamage( 25 );\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "}\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" )

Detailed Description

Public Variables

MRandomLCG sgLightningRand 

Public Functions

cmpSounds(const void * p1, const void * p2)

ConsoleDocClass(Lightning , "@brief An emitter <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> lightning <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bolts.\n\n</a>" "<a href="/coding/class/classlightning/">Lightning</a> strike events are created on the server and transmitted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> all " "clients <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> the bolt. The strike may be followed by <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> random thunder " "sound. <a href="/coding/class/classplayer/">Player</a> or <a href="/coding/class/classvehicle/">Vehicle</a> objects within the <a href="/coding/class/classlightning/">Lightning</a> strike range can be " "hit and damaged by <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bolts.\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">LightningData\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Atmosphere\n</a>" )

ConsoleDocClass(LightningData , "@brief Common data <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classlightning/">Lightning</a> emitter <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Lightning\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Atmosphere\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )

ConsoleDocClass(LightningStrikeEvent , "@brief Network event that triggers <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> lightning strike on the client when it " "is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">received.\n\n</a>" "This event is sent <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> all clients when the  warningFlashes, " "strikeRandomPoint() or strikeObject() methods are invoked on the <a href="/coding/class/classlightning/">Lightning</a> " "object on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">server.\n</a>" " @see Lightning, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">LightningData\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Atmosphere\n</a>" )

DefineEngineMethod(Lightning , strikeObject , void , (ShapeBase *pSB) , (nullAsType< ShapeBase * >()) , "Creates <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classlightningstrikeevent/">LightningStrikeEvent</a> which strikes <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> specific <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@note This method is currently <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">unimplemented.\n</a>" )

DefineEngineMethod(Lightning , strikeRandomPoint , void , () , "Creates <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classlightningstrikeevent/">LightningStrikeEvent</a> which attempts <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> strike and damage <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> random " "object in range of the <a href="/coding/class/classlightning/">Lightning</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Generate <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> damaging lightning strike effect on all <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">clients\n</a>" "%lightning.strikeRandomPoint();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@endtsexample" )

DefineEngineMethod(Lightning , warningFlashes , void , () , "@brief Creates <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classlightningstrikeevent/">LightningStrikeEvent</a> that triggers harmless lightning " "bolts on all <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">clients.\n</a>" "No objects will be damaged by these <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bolts.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Generate <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> harmless lightning strike effect on all <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">clients\n</a>" "%lightning.warningFlashes();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@endtsexample" )

IMPLEMENT_CALLBACK(Lightning , applyDamage , void , (const Point3F &hitPosition, const Point3F &hitNormal, SceneObject *hitObject) , (hitPosition, hitNormal, hitObject) , "Informs an object that it was hit by <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> lightning bolt and needs <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> take <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">damage.\n</a>" "@param hitPosition <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a81f4537631c9ab219ec74de554483adc">World</a> position hit by the lightning <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bolt.\n</a>" "@param hitNormal Surface normal at @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">hitPosition.\n</a>" "@param hitObject <a href="/coding/class/classplayer/">Player</a> or <a href="/coding/class/classvehicle/">Vehicle</a> object that was <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">hit.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "function Lightning::applyDamage( %this, %hitPosition, %hitNormal, %hitObject )\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   // apply damage <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">player\n</a>" "   %hitObject.applyDamage( 25 );\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "}\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" )

IMPLEMENT_CO_CLIENTEVENT_V1(LightningStrikeEvent )

IMPLEMENT_CO_DATABLOCK_V1(LightningData )

IMPLEMENT_CO_NETOBJECT_V1(Lightning )

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "platform/platform.h"
  25#include "T3D/fx/lightning.h"
  26
  27#include "scene/sceneRenderState.h"
  28#include "console/consoleTypes.h"
  29#include "math/mathIO.h"
  30#include "core/stream/bitStream.h"
  31#include "T3D/gameBase/gameConnection.h"
  32#include "T3D/shapeBase.h"
  33#include "math/mRandom.h"
  34#include "math/mathUtils.h"
  35#include "terrain/terrData.h"
  36#include "scene/sceneManager.h"
  37#include "T3D/player.h"
  38#include "T3D/camera.h"
  39#include "sfx/sfxSystem.h"
  40#include "sfx/sfxTrack.h"
  41#include "sfx/sfxTypes.h"
  42#include "gfx/primBuilder.h"
  43#include "console/engineAPI.h"
  44
  45
  46IMPLEMENT_CO_DATABLOCK_V1(LightningData);
  47IMPLEMENT_CO_NETOBJECT_V1(Lightning);
  48
  49ConsoleDocClass( LightningData,
  50   "@brief Common data for a Lightning emitter object.\n"
  51   "@see Lightning\n"
  52   "@ingroup FX\n"
  53   "@ingroup Atmosphere\n"
  54   "@ingroup Datablocks\n"
  55);
  56
  57ConsoleDocClass( Lightning,
  58   "@brief An emitter for lightning bolts.\n\n"
  59
  60   "Lightning strike events are created on the server and transmitted to all "
  61   "clients to render the bolt. The strike may be followed by a random thunder "
  62   "sound. Player or Vehicle objects within the Lightning strike range can be "
  63   "hit and damaged by bolts.\n"
  64
  65   "@see LightningData\n"
  66   "@ingroup FX\n"
  67   "@ingroup Atmosphere\n"
  68);
  69
  70IMPLEMENT_CALLBACK( Lightning, applyDamage, void, ( const Point3F& hitPosition, const Point3F& hitNormal, SceneObject* hitObject ),
  71   ( hitPosition, hitNormal, hitObject ),
  72   "Informs an object that it was hit by a lightning bolt and needs to take damage.\n"
  73   "@param hitPosition World position hit by the lightning bolt.\n"
  74   "@param hitNormal Surface normal at @a hitPosition.\n"
  75   "@param hitObject Player or Vehicle object that was hit.\n"
  76   "@tsexample\n"
  77   "function Lightning::applyDamage( %this, %hitPosition, %hitNormal, %hitObject )\n"
  78   "{\n"
  79   "   // apply damage to the player\n"
  80   "   %hitObject.applyDamage( 25 );\n"
  81   "}\n"
  82   "@endtsexample\n"
  83);
  84
  85
  86MRandomLCG sgLightningRand;
  87
  88
  89S32 QSORT_CALLBACK cmpSounds(const void* p1, const void* p2)
  90{
  91   U32 i1 = *((const S32*)p1);
  92   U32 i2 = *((const S32*)p2);
  93
  94   if (i1 < i2) {
  95      return 1;
  96   } else if (i1 > i2) {
  97      return -1;
  98   } else {
  99      return 0;
 100   }
 101}
 102
 103//--------------------------------------------------------------------------
 104//--------------------------------------
 105//
 106class LightningStrikeEvent : public NetEvent
 107{
 108  public:
 109   typedef NetEvent Parent;
 110   enum EventType {
 111      WarningFlash   = 0,
 112      Strike         = 1,
 113      TargetedStrike = 2,
 114
 115      TypeMin        = WarningFlash,
 116      TypeMax        = TargetedStrike
 117   };
 118   enum Constants {
 119      PositionalBits = 10
 120   };
 121
 122   Point2F                   mStart;
 123   SimObjectPtr<SceneObject> mTarget;
 124
 125   Lightning*                mLightning;
 126
 127   // Set by unpack...
 128  public:
 129   S32                       mClientId;
 130
 131  public:
 132   LightningStrikeEvent();
 133   ~LightningStrikeEvent();
 134
 135   void pack(NetConnection*, BitStream*);
 136   void write(NetConnection*, BitStream*){}
 137   void unpack(NetConnection*, BitStream*);
 138   void process(NetConnection*);
 139
 140   DECLARE_CONOBJECT(LightningStrikeEvent);
 141};
 142
 143IMPLEMENT_CO_CLIENTEVENT_V1(LightningStrikeEvent);
 144
 145ConsoleDocClass( LightningStrikeEvent,
 146   "@brief Network event that triggers a lightning strike on the client when it "
 147   "is received.\n\n"
 148   "This event is sent to all clients when the warningFlashes(), "
 149   "strikeRandomPoint() or strikeObject() methods are invoked on the Lightning "
 150   "object on the server.\n"
 151   "@see Lightning, LightningData\n"
 152   "@ingroup FX\n"
 153   "@ingroup Atmosphere\n"
 154);
 155
 156LightningStrikeEvent::LightningStrikeEvent()
 157{
 158   mLightning = NULL;
 159   mTarget = NULL;
 160   mClientId = 0;
 161}
 162
 163LightningStrikeEvent::~LightningStrikeEvent()
 164{
 165}
 166
 167void LightningStrikeEvent::pack(NetConnection* con, BitStream* stream)
 168{
 169   if(!mLightning)
 170   {
 171      stream->writeFlag(false);
 172      return;
 173   }
 174   S32 id = con->getGhostIndex(mLightning);
 175   if(id == -1)
 176   {
 177      stream->writeFlag(false);
 178      return;
 179   }
 180   stream->writeFlag(true);
 181   stream->writeRangedU32(U32(id), 0, NetConnection::MaxGhostCount);
 182   stream->writeFloat(mStart.x, PositionalBits);
 183   stream->writeFloat(mStart.y, PositionalBits);
 184
 185   if( mTarget )
 186   {
 187      S32 ghostIndex = con->getGhostIndex(mTarget);
 188      if (ghostIndex == -1)
 189         stream->writeFlag(false);
 190      else
 191      {
 192         stream->writeFlag(true);
 193         stream->writeRangedU32(U32(ghostIndex), 0, NetConnection::MaxGhostCount);
 194      }
 195   }
 196   else
 197      stream->writeFlag( false );
 198}
 199
 200void LightningStrikeEvent::unpack(NetConnection* con, BitStream* stream)
 201{
 202   if(!stream->readFlag())
 203      return;
 204   mClientId = stream->readRangedU32(0, NetConnection::MaxGhostCount);
 205   mLightning = NULL;
 206   NetObject* pObject = con->resolveGhost(mClientId);
 207   if (pObject)
 208      mLightning = dynamic_cast<Lightning*>(pObject);
 209
 210   mStart.x = stream->readFloat(PositionalBits);
 211   mStart.y = stream->readFloat(PositionalBits);
 212
 213   if( stream->readFlag() )
 214   {
 215      // target id
 216      S32 mTargetID    = stream->readRangedU32(0, NetConnection::MaxGhostCount);
 217
 218      NetObject* tObject = con->resolveGhost(mTargetID);
 219      if(tObject != NULL )
 220      {
 221         mTarget = dynamic_cast<SceneObject*>(tObject);
 222      }
 223      if( bool(mTarget) == false )
 224      {
 225         Con::errorf(ConsoleLogEntry::General, "LightningStrikeEvent::unpack: could not resolve target ghost properly");
 226      }
 227   }
 228}
 229
 230void LightningStrikeEvent::process(NetConnection*)
 231{
 232   if (mLightning)
 233      mLightning->processEvent(this);
 234}
 235
 236
 237//--------------------------------------------------------------------------
 238//--------------------------------------
 239//
 240LightningData::LightningData()
 241{
 242   strikeSound = NULL;
 243
 244   for (S32 i = 0; i < MaxThunders; i++)
 245      thunderSounds[i] = NULL;
 246
 247   for (S32 i = 0; i < MaxTextures; i++)
 248   {
 249      strikeTextureNames[i] = NULL;
 250      strikeTextures[i] = NULL;
 251   }
 252   numThunders = 0;
 253   mNumStrikeTextures = 0;
 254}
 255
 256LightningData::~LightningData()
 257{
 258
 259}
 260
 261//--------------------------------------------------------------------------
 262void LightningData::initPersistFields()
 263{
 264   addField( "strikeSound", TYPEID< SFXTrack >(), Offset(strikeSound, LightningData),
 265      "Sound profile to play when a lightning strike occurs." );
 266   addField( "thunderSounds", TYPEID< SFXTrack >(), Offset(thunderSounds, LightningData), MaxThunders,
 267      "@brief List of thunder sound effects to play.\n\n"
 268      "A random one of these sounds will be played shortly after each strike "
 269      "occurs." );
 270   addField( "strikeTextures", TypeString, Offset(strikeTextureNames, LightningData), MaxTextures,
 271      "List of textures to use to render lightning strikes." );
 272
 273   Parent::initPersistFields();
 274}
 275
 276
 277//--------------------------------------------------------------------------
 278bool LightningData::onAdd()
 279{
 280   if(!Parent::onAdd())
 281      return false;
 282
 283   return true;
 284}
 285
 286
 287bool LightningData::preload(bool server, String &errorStr)
 288{
 289   if (Parent::preload(server, errorStr) == false)
 290      return false;
 291
 292   //dQsort(thunderSounds, MaxThunders, sizeof(SFXTrack*), cmpSounds);
 293
 294   for (S32 i = 0; i < MaxThunders; i++) {
 295      if (thunderSounds[i]!= NULL) numThunders++;
 296   }
 297
 298   if (server == false) 
 299   {
 300      String sfxErrorStr;
 301      for (U32 i = 0; i < MaxThunders; i++) {
 302         if( !sfxResolve( &thunderSounds[ i ], sfxErrorStr ) )
 303            Con::errorf(ConsoleLogEntry::General, "LightningData::preload: Invalid packet: %s", sfxErrorStr.c_str());
 304      }
 305
 306      if( !sfxResolve( &strikeSound, sfxErrorStr ) )
 307         Con::errorf(ConsoleLogEntry::General, "LightningData::preload: Invalid packet: %s", sfxErrorStr.c_str());
 308
 309      mNumStrikeTextures = 0;
 310      for (U32 i = 0; i < MaxTextures; i++) 
 311      {
 312         if (strikeTextureNames[i][0])
 313         {
 314            strikeTextures[i] = GFXTexHandle(strikeTextureNames[i], &GFXStaticTextureProfile, avar("%s() - strikeTextures[%d] (line %d)", __FUNCTION__, i, __LINE__));
 315            mNumStrikeTextures++;
 316         }
 317      }
 318   }
 319
 320
 321   return true;
 322}
 323
 324
 325//--------------------------------------------------------------------------
 326void LightningData::packData(BitStream* stream)
 327{
 328   Parent::packData(stream);
 329
 330   U32 i;
 331   for (i = 0; i < MaxThunders; i++)
 332   {
 333      if (stream->writeFlag(thunderSounds[i]))
 334         sfxWrite(stream, thunderSounds[i]);
 335   }
 336
 337   stream->writeInt(mNumStrikeTextures, 4);
 338
 339   for (i = 0; i < MaxTextures; i++)
 340      stream->writeString(strikeTextureNames[i]);
 341
 342   sfxWrite( stream, strikeSound );
 343}
 344
 345void LightningData::unpackData(BitStream* stream)
 346{
 347   Parent::unpackData(stream);
 348
 349   U32 i;
 350   for (i = 0; i < MaxThunders; i++)
 351   {
 352      if (stream->readFlag())
 353         sfxRead(stream, &thunderSounds[i]);
 354      else
 355         thunderSounds[i] = NULL;
 356   }
 357
 358   mNumStrikeTextures = stream->readInt(4);
 359
 360   for (i = 0; i < MaxTextures; i++)
 361      strikeTextureNames[i] = stream->readSTString();
 362
 363   sfxRead( stream, &strikeSound );
 364}
 365
 366
 367//--------------------------------------------------------------------------
 368//--------------------------------------
 369//
 370Lightning::Lightning()
 371{
 372   mNetFlags.set(Ghostable</a>|<a href="/coding/class/classnetobject/#classnetobject_1ab7f7b94af4c238c69f5673a98199a01caa501b2b8ffe184a0a58342a1b9790258">ScopeAlways);
 373   mTypeMask |= StaticObjectType</a>|<a href="/coding/file/objecttypes_8h/#objecttypes_8h_1ac18d4a11e9446825e48fd474d7529084a43891480c40ab2b66f5d17713401a340">EnvironmentObjectType;
 374
 375   mDataBlock = NULL;
 376   mLastThink = 0;
 377
 378   mStrikeListHead  = NULL;
 379   mThunderListHead = NULL;
 380
 381   strikesPerMinute = 12;
 382   strikeWidth = 2.5;
 383   chanceToHitTarget = 0.5f;
 384   strikeRadius = 20.0f;
 385   boltStartRadius = 20.0f;
 386   color.set( 1.0f, 1.0f, 1.0f, 1.0f );
 387   fadeColor.set( 0.1f, 0.1f, 1.0f, 1.0f );
 388   useFog = true;
 389
 390   setScale( VectorF( 512.0f, 512.0f, 300.0f ) );
 391}
 392
 393Lightning::~Lightning()
 394{
 395   while( mThunderListHead )
 396   {
 397      Thunder* nextThunder = mThunderListHead->next;
 398      delete mThunderListHead;
 399      mThunderListHead = nextThunder;
 400   }
 401
 402   while( mStrikeListHead )
 403   {
 404      Strike* nextStrike = mStrikeListHead->next;
 405      delete mStrikeListHead;
 406      mStrikeListHead = nextStrike;
 407   }
 408}
 409
 410//--------------------------------------------------------------------------
 411void Lightning::initPersistFields()
 412{
 413   addGroup( "Strikes" );
 414   addField( "strikesPerMinute", TypeS32, Offset(strikesPerMinute, Lightning),
 415      "@brief Number of lightning strikes to perform per minute.\n\n"
 416      "Automatically invokes strikeRandomPoint() at regular intervals." );
 417   addField( "strikeWidth", TypeF32, Offset(strikeWidth, Lightning),
 418      "Width of a lightning bolt." );
 419   addField( "strikeRadius", TypeF32, Offset(strikeRadius, Lightning),
 420      "@brief Horizontal size (XY plane) of the search box used to find and "
 421      "damage Player or Vehicle objects within range of the strike.\n\n"
 422      "Only the object at highest altitude with a clear line of sight to the "
 423      "bolt will be hit." );
 424   endGroup( "Strikes" );
 425
 426   addGroup( "Colors" );
 427   addField( "color", TypeColorF, Offset(color, Lightning),
 428      "Color to blend the strike texture with." );
 429   addField( "fadeColor", TypeColorF, Offset(fadeColor, Lightning),
 430      "@brief Color to blend the strike texture with when the bolt is fading away.\n\n"
 431      "Bolts fade away automatically shortly after the strike occurs." );
 432   endGroup( "Colors" );
 433
 434   addGroup( "Bolts" );
 435   addField( "chanceToHitTarget", TypeF32, Offset(chanceToHitTarget, Lightning),
 436      "Percentage chance (0-1) that a given lightning bolt will hit something." );
 437   addField( "boltStartRadius", TypeF32, Offset(boltStartRadius, Lightning),
 438      "@brief Radial distance from the center of the Lightning object for the "
 439      "start point of the bolt.\n\n"
 440      "The actual start point will be a random point within this radius." );
 441   addField( "useFog", TypeBool, Offset(useFog, Lightning),
 442      "Controls whether lightning bolts are affected by fog when they are rendered." );
 443   endGroup( "Bolts" );
 444
 445   Parent::initPersistFields();
 446}
 447
 448//--------------------------------------------------------------------------
 449bool Lightning::onAdd()
 450{
 451   if(!Parent::onAdd())
 452      return false;
 453
 454   mObjBox.minExtents.set( -0.5f, -0.5f, -0.5f );
 455   mObjBox.maxExtents.set(  0.5f,  0.5f,  0.5f );
 456
 457   resetWorldBox();
 458   addToScene();
 459
 460   return true;
 461}
 462
 463
 464void Lightning::onRemove()
 465{
 466   removeFromScene();
 467
 468   Parent::onRemove();
 469}
 470
 471
 472bool Lightning::onNewDataBlock( GameBaseData *dptr, bool reload )
 473{
 474   mDataBlock = dynamic_cast<LightningData*>( dptr );
 475   if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
 476      return false;
 477
 478   scriptOnNewDataBlock();
 479   return true;
 480}
 481
 482
 483//--------------------------------------------------------------------------
 484void Lightning::prepRenderImage( SceneRenderState* state )
 485{
 486   ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
 487   ri->renderDelegate.bind(this, &Lightning::renderObject);
 488   // The Lightning isn't technically foliage but our debug
 489   // effect seems to render best as a Foliage type (translucent,
 490   // renders itself, no sorting)
 491   ri->type = RenderPassManager::RIT_Foliage;
 492   state->getRenderPass()->addInst( ri );
 493}
 494
 495
 496void Lightning::renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat)
 497{
 498   if (overrideMat)
 499      return;
 500
 501   if (mLightningSB.isNull())
 502   {
 503      GFXStateBlockDesc desc;
 504      desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendOne);
 505      desc.setCullMode(GFXCullNone);
 506      desc.zWriteEnable = false;
 507      desc.vertexColorEnable = true;
 508
 509      if (mDataBlock->mNumStrikeTextures != 0)
 510      {
 511         desc.samplersDefined = true;
 512         desc.samplers[0].magFilter = GFXTextureFilterLinear;
 513         desc.samplers[0].minFilter = GFXTextureFilterLinear;
 514         desc.samplers[0].addressModeU = GFXAddressWrap;
 515         desc.samplers[0].addressModeV = GFXAddressWrap;
 516      }
 517
 518      mLightningSB = GFX->createStateBlock(desc);
 519
 520   }
 521
 522   GFX->setStateBlock(mLightningSB);
 523
 524
 525   Strike* walk = mStrikeListHead;
 526   while (walk != NULL)
 527   {
 528      if (mDataBlock->mNumStrikeTextures > 1)
 529      {
 530         GFX->setTexture(0, mDataBlock->strikeTextures[sgLightningRand.randI(0, mDataBlock->mNumStrikeTextures - 1)]);
 531      }
 532      else if (mDataBlock->mNumStrikeTextures > 0)
 533      {
 534         GFX->setTexture(0, mDataBlock->strikeTextures[0]);
 535      }
 536
 537      for( U32 i=0; i<MAX_LIGHTNING; i++ )
 538      {
 539         if( walk->bolt[i].isFading )
 540         {
 541            F32 alpha = 1.0f - walk->bolt[i].percentFade;
 542            if( alpha < 0.0f ) alpha = 0.0f;
 543            PrimBuild::color4f( fadeColor.red, fadeColor.green, fadeColor.blue, alpha );
 544         }
 545         else
 546         {
 547            PrimBuild::color4f( color.red, color.green, color.blue, color.alpha );
 548         }
 549         walk->bolt[i].render( state->getCameraPosition() );
 550      }
 551
 552      walk = walk->next;
 553   }
 554
 555   //GFX->setZWriteEnable(true);
 556   //GFX->setAlphaTestEnable(false);
 557   //GFX->setAlphaBlendEnable(false);
 558}
 559
 560void Lightning::scheduleThunder(Strike* newStrike)
 561{
 562   AssertFatal(isClientObject(), "Lightning::scheduleThunder: server objects should not enter this version of the function");
 563
 564   // If no thunder sounds, don't schedule anything!
 565   if (mDataBlock->numThunders == 0)
 566      return;
 567
 568   GameConnection* connection = GameConnection::getConnectionToServer();
 569   if (connection) {
 570      MatrixF cameraMatrix;
 571
 572      if (connection->getControlCameraTransform(0, &cameraMatrix)) {
 573         Point3F worldPos;
 574         cameraMatrix.getColumn(3, &worldPos);
 575
 576         worldPos.x -= newStrike->xVal;
 577         worldPos.y -= newStrike->yVal;
 578         worldPos.z  = 0.0f;
 579
 580         F32 dist = worldPos.len();
 581         F32 t    = dist / 330.0f;
 582
 583         // Ok, we need to schedule a random strike sound t secs in the future...
 584         //
 585         if (t <= 0.03f) {
 586            // If it's really close, just play it...
 587            U32 thunder = sgLightningRand.randI(0, mDataBlock->numThunders - 1);
 588            SFX->playOnce(mDataBlock->thunderSounds[thunder]);
 589         } else {
 590            Thunder* pThunder = new Thunder;
 591            pThunder->tRemaining = t;
 592            pThunder->next       = mThunderListHead;
 593            mThunderListHead     = pThunder;
 594         }
 595      }
 596   }
 597}
 598
 599
 600//--------------------------------------------------------------------------
 601void Lightning::processTick(const Move* move)
 602{
 603   Parent::processTick(move);
 604
 605   if (isServerObject() && !isHidden()) {
 606      S32 msBetweenStrikes = (S32)(60.0 / strikesPerMinute * 1000.0);
 607
 608      mLastThink += TickMs;
 609      if( mLastThink > msBetweenStrikes )
 610      {
 611         strikeRandomPoint();
 612         mLastThink -= msBetweenStrikes;
 613      }
 614   }
 615}
 616
 617void Lightning::interpolateTick(F32 dt)
 618{
 619   Parent::interpolateTick(dt);
 620}
 621
 622void Lightning::advanceTime(F32 dt)
 623{
 624   Parent::advanceTime(dt);
 625
 626   Strike** pWalker = &mStrikeListHead;
 627   while (*pWalker != NULL) {
 628      Strike* pStrike = *pWalker;
 629
 630      for( U32 i=0; i<MAX_LIGHTNING; i++ )
 631      {
 632         pStrike->bolt[i].update( dt );
 633      }
 634
 635      pStrike->currentAge += dt;
 636      if (pStrike->currentAge > pStrike->deathAge) {
 637         *pWalker = pStrike->next;
 638         delete pStrike;
 639      } else {
 640         pWalker = &((*pWalker)->next);
 641      }
 642   }
 643
 644   Thunder** pThunderWalker = &mThunderListHead;
 645   while (*pThunderWalker != NULL) {
 646      Thunder* pThunder = *pThunderWalker;
 647
 648      pThunder->tRemaining -= dt;
 649      if (pThunder->tRemaining <= 0.0f) {
 650         *pThunderWalker = pThunder->next;
 651         delete pThunder;
 652
 653         // Play the sound...
 654         U32 thunder = sgLightningRand.randI(0, mDataBlock->numThunders - 1);
 655         SFX->playOnce(mDataBlock->thunderSounds[thunder]);
 656      } else {
 657         pThunderWalker = &((*pThunderWalker)->next);
 658      }
 659   }
 660}
 661
 662
 663//--------------------------------------------------------------------------
 664void Lightning::processEvent(LightningStrikeEvent* pEvent)
 665{
 666      AssertFatal(pEvent->mStart.x >= 0.0f && pEvent->mStart.x <= 1.0f, "Out of bounds coord!");
 667
 668      Strike* pStrike = new Strike;
 669
 670      Point3F strikePoint;
 671      strikePoint.zero();
 672
 673      if( pEvent->mTarget )
 674      {
 675         Point3F objectCenter;
 676         pEvent->mTarget->getObjBox().getCenter( &objectCenter );
 677         objectCenter.convolve( pEvent->mTarget->getScale() );
 678         pEvent->mTarget->getTransform().mulP( objectCenter );
 679
 680         strikePoint = objectCenter;
 681      }
 682      else
 683      {
 684         strikePoint.x = pEvent->mStart.x;
 685         strikePoint.y = pEvent->mStart.y;
 686         strikePoint *= mObjScale;
 687         strikePoint += getPosition();
 688         strikePoint += Point3F( -mObjScale.x * 0.5f, -mObjScale.y * 0.5f, 0.0f );
 689
 690         RayInfo rayInfo;
 691         Point3F start = strikePoint;
 692         start.z = mObjScale.z * 0.5f + getPosition().z;
 693         strikePoint.z += -mObjScale.z * 0.5f;
 694         bool rayHit = gClientContainer.castRay( start, strikePoint,
 695                                      (STATIC_COLLISION_TYPEMASK | WaterObjectType),
 696                                      &rayInfo);
 697         if( rayHit )
 698         {
 699            strikePoint.z = rayInfo.point.z;
 700         }
 701         else
 702         {
 703            strikePoint.z = pStrike->bolt[0].findHeight( strikePoint, getSceneManager() );
 704         }
 705      }
 706
 707      pStrike->xVal       = strikePoint.x;
 708      pStrike->yVal       = strikePoint.y;
 709
 710      pStrike->deathAge   = 1.6f;
 711      pStrike->currentAge = 0.0f;
 712      pStrike->next       = mStrikeListHead;
 713
 714      for( U32 i=0; i<MAX_LIGHTNING; i++ )
 715      {
 716         F32 randStart = boltStartRadius;
 717         F32 height = mObjScale.z * 0.5f + getPosition().z;
 718         pStrike->bolt[i].startPoint.set( pStrike->xVal + gRandGen.randF( -randStart, randStart ), pStrike->yVal + gRandGen.randF( -randStart, randStart ), height );
 719         pStrike->bolt[i].endPoint = strikePoint;
 720         pStrike->bolt[i].width = strikeWidth;
 721         pStrike->bolt[i].numMajorNodes = 10;
 722         pStrike->bolt[i].maxMajorAngle = 30.0f;
 723         pStrike->bolt[i].numMinorNodes = 4;
 724         pStrike->bolt[i].maxMinorAngle = 15.0f;
 725         pStrike->bolt[i].generate();
 726         pStrike->bolt[i].startSplits();
 727         pStrike->bolt[i].lifetime = 1.0f;
 728         pStrike->bolt[i].fadeTime = 0.2f;
 729         pStrike->bolt[i].renderTime = gRandGen.randF(0.0f, 0.25f);
 730      }
 731
 732      mStrikeListHead     = pStrike;
 733
 734      scheduleThunder(pStrike);
 735
 736      MatrixF trans(true);
 737      trans.setPosition( strikePoint );
 738
 739      if (mDataBlock->strikeSound)
 740      {
 741         SFX->playOnce(mDataBlock->strikeSound, &trans );
 742      }
 743
 744}
 745
 746void Lightning::warningFlashes()
 747{
 748   AssertFatal(isServerObject(), "Error, client objects may not initiate lightning!");
 749
 750   Point3F strikePoint( gRandGen.randF( 0.0f, 1.0f ), gRandGen.randF( 0.0f, 1.0f ), 0.0f );
 751
 752   SimGroup* pClientGroup = Sim::getClientGroup();
 753   for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++) {
 754      NetConnection* nc = static_cast<NetConnection*>(*itr);
 755      if (nc != NULL)
 756      {
 757         LightningStrikeEvent* pEvent = new LightningStrikeEvent;
 758         pEvent->mLightning = this;
 759       
 760       pEvent->mStart.x = strikePoint.x;
 761       pEvent->mStart.y = strikePoint.y;
 762
 763         nc->postNetEvent(pEvent);
 764      }
 765   }
 766}
 767
 768void Lightning::strikeRandomPoint()
 769{
 770   AssertFatal(isServerObject(), "Error, client objects may not initiate lightning!");
 771
 772
 773   Point3F strikePoint( gRandGen.randF( 0.0f, 1.0f ), gRandGen.randF( 0.0f, 1.0f ), 0.0f );
 774
 775   // check if an object is within target range
 776   Point3F worldPosStrikePoint = strikePoint;
 777
 778   worldPosStrikePoint *= mObjScale;
 779   worldPosStrikePoint += getPosition();
 780   worldPosStrikePoint += Point3F( -mObjScale.x * 0.5f, -mObjScale.y * 0.5f, 0.0f );
 781
 782   Box3F queryBox;
 783   F32 boxWidth = strikeRadius * 2.0f;
 784
 785   queryBox.minExtents.set( -boxWidth * 0.5f, -boxWidth * 0.5f, -mObjScale.z * 0.5f );
 786   queryBox.maxExtents.set(  boxWidth * 0.5f,  boxWidth * 0.5f,  mObjScale.z * 0.5f );
 787   queryBox.minExtents += worldPosStrikePoint;
 788   queryBox.maxExtents += worldPosStrikePoint;
 789
 790   SimpleQueryList sql;
 791   getContainer()->findObjects(queryBox, DAMAGEABLE_TYPEMASK,
 792                               SimpleQueryList::insertionCallback, &sql);
 793
 794   SceneObject *highestObj = NULL;
 795   F32 highestPnt = 0.0f;
 796
 797   for( U32 i = 0; i < sql.mList.size(); i++ )
 798   {
 799      Point3F objectCenter;
 800      sql.mList[i]->getObjBox().getCenter(&objectCenter);
 801      objectCenter.convolve(sql.mList[i]->getScale());
 802      sql.mList[i]->getTransform().mulP(objectCenter);
 803
 804      // check if object can be struck
 805
 806      RayInfo rayInfo;
 807      Point3F start = objectCenter;
 808      start.z = mObjScale.z * 0.5f + getPosition().z;
 809      Point3F end = objectCenter;
 810      end.z = -mObjScale.z * 0.5f + getPosition().z;
 811      bool rayHit = gServerContainer.castRay( start, end,
 812                                   (0xFFFFFFFF),
 813                                   &rayInfo);
 814
 815      if( rayHit && rayInfo.object == sql.mList[i] )
 816      {
 817         if( !highestObj )
 818         {
 819            highestObj = sql.mList[i];
 820            highestPnt = objectCenter.z;
 821            continue;
 822         }
 823
 824         if( objectCenter.z > highestPnt )
 825         {
 826            highestObj = sql.mList[i];
 827            highestPnt = objectCenter.z;
 828         }
 829      }
 830
 831
 832   }
 833
 834   // hah haaaaa, we have a target!
 835   SceneObject *targetObj = NULL;
 836   if( highestObj )
 837   {
 838      F32 chance = gRandGen.randF();
 839      if( chance <= chanceToHitTarget )
 840      {
 841         Point3F objectCenter;
 842         highestObj->getObjBox().getCenter(&objectCenter);
 843         objectCenter.convolve(highestObj->getScale());
 844         highestObj->getTransform().mulP(objectCenter);
 845
 846         bool playerInWarmup = false;
 847         Player *playerObj = dynamic_cast< Player * >(highestObj);
 848         if( playerObj )
 849         {
 850            if( !playerObj->getControllingClient() )
 851            {
 852               playerInWarmup = true;
 853            }
 854         }
 855
 856         if( !playerInWarmup )
 857         {
 858            applyDamage_callback( objectCenter, VectorF( 0.0f, 0.0f, 1.0f ), highestObj );
 859            targetObj = highestObj;
 860         }
 861      }
 862   }
 863
 864   SimGroup* pClientGroup = Sim::getClientGroup();
 865   for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++)
 866   {
 867      NetConnection* nc = static_cast<NetConnection*>(*itr);
 868
 869      LightningStrikeEvent* pEvent = new LightningStrikeEvent;
 870      pEvent->mLightning = this;
 871
 872      pEvent->mStart.x = strikePoint.x;
 873      pEvent->mStart.y = strikePoint.y;
 874      pEvent->mTarget = targetObj;
 875
 876      nc->postNetEvent(pEvent);
 877   }
 878
 879
 880}
 881
 882//--------------------------------------------------------------------------
 883void Lightning::strikeObject(ShapeBase* targetObj)
 884{
 885   AssertFatal(isServerObject(), "Error, client objects may not initiate lightning!");
 886
 887   Point3F strikePoint = targetObj->getPosition();
 888   Point3F objectCenter;
 889
 890   Box3F wb = getWorldBox();
 891   if (!wb.isContained(strikePoint))
 892        return;
 893
 894   Point3F targetRel = strikePoint - getPosition();
 895   Point3F length(wb.len_x() / 2.0f, wb.len_y() / 2.0f, wb.len_z() / 2.0f);
 896
 897   Point3F strikePos = targetRel / length;
 898
 899   bool playerInWarmup = false;
 900   Player *playerObj = dynamic_cast< Player * >(targetObj);
 901   if (playerObj)
 902   {
 903       if (!playerObj->getControllingClient())
 904       {
 905           playerInWarmup = true;
 906       }
 907   }
 908
 909   if (!playerInWarmup)
 910   {
 911       applyDamage_callback(targetObj->getWorldSphere().center, VectorF(0.0, 0.0, 1.0), targetObj);
 912   }
 913 
 914   SimGroup* pClientGroup = Sim::getClientGroup();
 915   for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++) {
 916      NetConnection* nc = static_cast<NetConnection*>(*itr);
 917      if (nc != NULL)
 918      {
 919         LightningStrikeEvent* pEvent = new LightningStrikeEvent;
 920         pEvent->mLightning = this;
 921       
 922         pEvent->mStart.x = strikePoint.x;
 923         pEvent->mStart.y = strikePoint.y;
 924         pEvent->mTarget = targetObj;
 925
 926         nc->postNetEvent(pEvent);
 927      }
 928   }
 929}
 930
 931//--------------------------------------------------------------------------
 932U32 Lightning::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
 933{
 934   U32 retMask = Parent::packUpdate(con, mask, stream);
 935
 936   // Only write data if this is the initial packet or we've been inspected.
 937   if (stream->writeFlag(mask & (InitialUpdateMask | ExtendedInfoMask)))
 938   {
 939      // Initial update
 940      mathWrite(*stream, getPosition());
 941      mathWrite(*stream, mObjScale);
 942
 943      stream->write(strikeWidth);
 944      stream->write(chanceToHitTarget);
 945      stream->write(strikeRadius);
 946      stream->write(boltStartRadius);
 947      stream->write(color.red);
 948      stream->write(color.green);
 949      stream->write(color.blue);
 950      stream->write(color.alpha);
 951      stream->write(fadeColor.red);
 952      stream->write(fadeColor.green);
 953      stream->write(fadeColor.blue);
 954      stream->write(useFog);
 955      stream->write(strikesPerMinute);
 956   }
 957
 958   return retMask;
 959}
 960
 961//--------------------------------------------------------------------------
 962void Lightning::unpackUpdate(NetConnection* con, BitStream* stream)
 963{
 964   Parent::unpackUpdate(con, stream);
 965
 966   if (stream->readFlag())
 967   {
 968      // Initial update
 969      Point3F pos;
 970      mathRead(*stream, &pos);
 971      setPosition( pos );
 972
 973      mathRead(*stream, &mObjScale);
 974
 975      stream->read(&strikeWidth);
 976      stream->read(&chanceToHitTarget);
 977      stream->read(&strikeRadius);
 978      stream->read(&boltStartRadius);
 979      stream->read(&color.red);
 980      stream->read(&color.green);
 981      stream->read(&color.blue);
 982      stream->read(&color.alpha);
 983      stream->read(&fadeColor.red);
 984      stream->read(&fadeColor.green);
 985      stream->read(&fadeColor.blue);
 986      stream->read(&useFog);
 987      stream->read(&strikesPerMinute);
 988   }
 989}
 990
 991//--------------------------------------------------------------------------
 992
 993DefineEngineMethod(Lightning, warningFlashes, void, (),,
 994   "@brief Creates a LightningStrikeEvent that triggers harmless lightning "
 995   "bolts on all clients.\n"
 996   "No objects will be damaged by these bolts.\n"
 997   "@tsexample\n"
 998   "// Generate a harmless lightning strike effect on all clients\n"
 999   "%lightning.warningFlashes();\n"
1000   "@endtsexample" )
1001{
1002   if (object->isServerObject()) 
1003      object->warningFlashes();
1004}
1005
1006DefineEngineMethod(Lightning, strikeRandomPoint, void, (),,
1007   "Creates a LightningStrikeEvent which attempts to strike and damage a random "
1008   "object in range of the Lightning object.\n"
1009   "@tsexample\n"
1010   "// Generate a damaging lightning strike effect on all clients\n"
1011   "%lightning.strikeRandomPoint();\n"
1012   "@endtsexample" )
1013{
1014   if (object->isServerObject()) 
1015      object->strikeRandomPoint();
1016}
1017
1018DefineEngineMethod(Lightning, strikeObject, void, (ShapeBase* pSB), (nullAsType<ShapeBase*>()),
1019   "Creates a LightningStrikeEvent which strikes a specific object.\n"
1020   "@note This method is currently unimplemented.\n" )
1021{
1022      object->strikeObject(pSB);
1023}
1024
1025//**************************************************************************
1026// Lightning Bolt
1027//**************************************************************************
1028LightningBolt::LightningBolt()
1029{
1030   width = 0.1f;
1031   startPoint.zero();
1032   endPoint.zero();
1033   chanceOfSplit = 0.0f;
1034   isFading = false;
1035   elapsedTime = 0.0f;
1036   lifetime = 1.0f;
1037   startRender = false;
1038   endPoint.zero();
1039   width = 1;
1040   numMajorNodes = 10;
1041   maxMajorAngle = 30.0f;
1042   numMinorNodes = 4;
1043   maxMinorAngle = 15.0f;
1044   fadeTime = 0.2f;
1045   renderTime = 0.125;
1046   dMemset(&mMajorNodes, 0, sizeof(mMajorNodes));
1047   percentFade = 0.0f;
1048}
1049
1050//--------------------------------------------------------------------------
1051// Destructor
1052//--------------------------------------------------------------------------
1053LightningBolt::~LightningBolt()
1054{
1055   splitList.clear();
1056}
1057
1058//--------------------------------------------------------------------------
1059// Generate nodes
1060//--------------------------------------------------------------------------
1061void LightningBolt::NodeManager::generateNodes()
1062{
1063   F32 overallDist = VectorF( endPoint - startPoint ).magnitudeSafe();
1064   F32 minDistBetweenNodes = overallDist / (numNodes-1);
1065   F32 maxDistBetweenNodes = minDistBetweenNodes / mCos( maxAngle * M_PI_F / 180.0f );
1066
1067   VectorF mainLineDir = endPoint - startPoint;
1068   mainLineDir.normalizeSafe();
1069
1070   for( U32 i=0; i<numNodes; i++ )
1071   {
1072      Node node;
1073
1074      if( i == 0 )
1075      {
1076         node.point = startPoint;
1077         node.dirToMainLine = mainLineDir;
1078         nodeList[i] = node;
1079         continue;
1080      }
1081      if( i == numNodes - 1 )
1082      {
1083         node.point = endPoint;
1084         nodeList[i] = node;
1085         break;
1086      }
1087
1088      Node lastNode = nodeList[i-1];
1089
1090      F32 segmentLength = gRandGen.randF( minDistBetweenNodes, maxDistBetweenNodes );
1091      VectorF segmentDir = MathUtils::randomDir( lastNode.dirToMainLine, 0, maxAngle );
1092      node.point = lastNode.point + segmentDir * segmentLength;
1093
1094      node.dirToMainLine = endPoint - node.point;
1095      node.dirToMainLine.normalizeSafe();
1096      nodeList[i] = node;
1097   }
1098}
1099
1100
1101//--------------------------------------------------------------------------
1102// Render bolt
1103//--------------------------------------------------------------------------
1104void LightningBolt::render( const Point3F &camPos )
1105{
1106   if (!startRender)
1107      return;
1108
1109   if (!isFading)
1110      generateMinorNodes();
1111
1112   U32 maxVerts = 0;
1113   for (U32 i = 0; i < mMinorNodes.size(); i++)
1114      maxVerts += mMinorNodes[i].numNodes * 2;
1115
1116   PrimBuild::begin(GFXTriangleStrip, maxVerts);
1117
1118   for (U32 i = 0; i < mMinorNodes.size(); i++)
1119   {
1120      if (i+1 == mMinorNodes.size())
1121         renderSegment(mMinorNodes[i], camPos, true);
1122      else
1123         renderSegment(mMinorNodes[i], camPos, false);
1124   }
1125
1126   PrimBuild::end();
1127
1128   for(LightingBoltList::Iterator i = splitList.begin(); i != splitList.end(); ++i)
1129   {
1130      if( isFading )
1131      {
1132         i->isFading = true;
1133      }
1134      i->render( camPos );
1135   }
1136
1137}
1138
1139//--------------------------------------------------------------------------
1140// Render segment
1141//--------------------------------------------------------------------------
1142void LightningBolt::renderSegment( NodeManager &segment, const Point3F &camPos, bool renderLastPoint )
1143{
1144
1145   for (U32 i = 0; i < segment.numNodes; i++)
1146   {
1147      Point3F  curPoint = segment.nodeList[i].point;
1148
1149      Point3F  nextPoint;
1150      Point3F  segDir;
1151
1152      if( i == (segment.numNodes-1) )
1153      {
1154         if( renderLastPoint )
1155         {
1156            segDir = curPoint - segment.nodeList[i-1].point;
1157         }
1158         else
1159         {
1160            continue;
1161         }
1162      }
1163      else
1164      {
1165         nextPoint = segment.nodeList[i+1].point;
1166         segDir = nextPoint - curPoint;
1167      }
1168      segDir.normalizeSafe();
1169
1170
1171      Point3F dirFromCam = curPoint - camPos;
1172      Point3F crossVec;
1173      mCross(dirFromCam, segDir, &crossVec);
1174      crossVec.normalize();
1175      crossVec *= width * 0.5f;
1176
1177      F32 u = i % 2;
1178
1179      PrimBuild::texCoord2f( u, 1.0 );
1180      PrimBuild::vertex3fv( curPoint - crossVec );
1181
1182      PrimBuild::texCoord2f( u, 0.0 );
1183      PrimBuild::vertex3fv( curPoint + crossVec );
1184   }
1185
1186}
1187
1188//----------------------------------------------------------------------------
1189// Find height
1190//----------------------------------------------------------------------------
1191F32 LightningBolt::findHeight( Point3F &point, SceneManager *sceneManager )
1192{
1193   const Vector< SceneObject*> terrains = sceneManager->getContainer()->getTerrains();
1194   for( Vector< SceneObject* >::const_iterator iter = terrains.begin(); iter != terrains.end(); ++ iter )
1195   {
1196      TerrainBlock* terrain = dynamic_cast< TerrainBlock* >( *iter );
1197      if( !terrain )
1198         continue;
1199
1200      Point3F terrPt = point;
1201      terrain->getWorldTransform().mulP(terrPt);
1202
1203      F32 h;
1204      if( terrain->getHeight( Point2F( terrPt.x, terrPt.y ), &h ) )
1205         return h;
1206   }
1207
1208   return 0.f;
1209}
1210
1211
1212//----------------------------------------------------------------------------
1213// Generate lightning bolt
1214//----------------------------------------------------------------------------
1215void LightningBolt::generate()
1216{
1217   mMajorNodes.startPoint   = startPoint;
1218   mMajorNodes.endPoint     = endPoint;
1219   mMajorNodes.numNodes     = numMajorNodes;
1220   mMajorNodes.maxAngle     = maxMajorAngle;
1221
1222   mMajorNodes.generateNodes();
1223
1224   generateMinorNodes();
1225
1226}
1227
1228//----------------------------------------------------------------------------
1229// Generate Minor Nodes
1230//----------------------------------------------------------------------------
1231void LightningBolt::generateMinorNodes()
1232{
1233   mMinorNodes.clear();
1234
1235   for( S32 i=0; i<mMajorNodes.numNodes - 1; i++ )
1236   {
1237      NodeManager segment;
1238      segment.startPoint = mMajorNodes.nodeList[i].point;
1239      segment.endPoint = mMajorNodes.nodeList[i+1].point;
1240      segment.numNodes = numMinorNodes;
1241      segment.maxAngle = maxMinorAngle;
1242      segment.generateNodes();
1243
1244      mMinorNodes.increment(1);
1245      mMinorNodes[i] = segment;
1246   }
1247}
1248
1249//----------------------------------------------------------------------------
1250// Recursive algo to create bolts that split off from main bolt
1251//----------------------------------------------------------------------------
1252void LightningBolt::createSplit( const Point3F &startingPoint, const Point3F &endingPoint, U32 depth, F32 splitWidth )
1253{
1254   if( depth == 0 )
1255      return;
1256     
1257   F32 chanceToEnd = gRandGen.randF();
1258   if( chanceToEnd > 0.70f )
1259      return;
1260
1261   if(splitWidth < 0.75f )
1262      splitWidth = 0.75f;
1263
1264   VectorF diff = endingPoint - startingPoint;
1265   F32 length = diff.len();
1266   diff.normalizeSafe();
1267
1268   LightningBolt newBolt;
1269   newBolt.startPoint = startingPoint;
1270   newBolt.endPoint = endingPoint;
1271   newBolt.width = splitWidth;
1272   newBolt.numMajorNodes = 3;
1273   newBolt.maxMajorAngle = 30.0f;
1274   newBolt.numMinorNodes = 3;
1275   newBolt.maxMinorAngle = 10.0f;
1276   newBolt.startRender = true;
1277   newBolt.generate();
1278
1279   splitList.pushBack( newBolt );
1280
1281   VectorF newDir1 = MathUtils::randomDir( diff, 10.0f, 45.0f );
1282   Point3F newEndPoint1 = endingPoint + newDir1 * gRandGen.randF( 0.5f, 1.5f ) * length;
1283
1284   VectorF newDir2 = MathUtils::randomDir( diff, 10.0f, 45.0f );
1285   Point3F newEndPoint2 = endingPoint + newDir2 * gRandGen.randF( 0.5f, 1.5f ) * length;
1286
1287   createSplit(endingPoint, newEndPoint1, depth - 1, splitWidth * 0.30f );
1288   createSplit(endingPoint, newEndPoint2, depth - 1, splitWidth * 0.30f );
1289
1290}
1291
1292//----------------------------------------------------------------------------
1293// Start split - kick off the recursive 'createSplit' procedure
1294//----------------------------------------------------------------------------
1295void LightningBolt::startSplits()
1296{
1297
1298   for( U32 i=0; i<mMajorNodes.numNodes-1; i++ )
1299   {
1300      if( gRandGen.randF() > 0.3f )
1301        continue;
1302
1303      Node node = mMajorNodes.nodeList[i];
1304      Node node2 = mMajorNodes.nodeList[i+1];
1305
1306      VectorF segDir = node2.point - node.point;
1307      F32 length = segDir.len();
1308      segDir.normalizeSafe();
1309
1310      VectorF newDir = MathUtils::randomDir( segDir, 20.0f, 40.0f );
1311      Point3F newEndPoint = node.point + newDir * gRandGen.randF( 0.5f, 1.5f ) * length;
1312
1313
1314      createSplit( node.point, newEndPoint, 4, width * 0.30f );
1315   }
1316
1317
1318}
1319
1320//----------------------------------------------------------------------------
1321// Update
1322//----------------------------------------------------------------------------
1323void LightningBolt::update( F32 dt )
1324{
1325   elapsedTime += dt;
1326
1327   F32 percentDone = elapsedTime / lifetime;
1328
1329   if( elapsedTime > fadeTime )
1330   {
1331      isFading = true;
1332      percentFade = percentDone + (fadeTime/<a href="/coding/class/structlightningbolt/#structlightningbolt_1af344b46100a4d07c2260a37e25e98bbe">lifetime</a>);
1333   }
1334
1335   if( elapsedTime > renderTime && !startRender )
1336   {
1337      startRender = true;
1338      isFading = false;
1339      elapsedTime = 0.0f;
1340   }
1341}
1342