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> render the bolt. The strike may be followed by 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 <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 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) , "Creates a <a href="/coding/class/classlightningstrikeevent/">LightningStrikeEvent</a> which strikes 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 <a href="/coding/class/classlightningstrikeevent/">LightningStrikeEvent</a> which attempts <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> strike and damage 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 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 <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 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 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 <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> render the bolt. The strike may be followed by 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 <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 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) , "Creates a <a href="/coding/class/classlightningstrikeevent/">LightningStrikeEvent</a> which strikes 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 <a href="/coding/class/classlightningstrikeevent/">LightningStrikeEvent</a> which attempts <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> strike and damage 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 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 <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 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 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 <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   S32 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* pObject = con->resolveGhost(mTargetID);
 219      if( pObject != NULL )
 220      {
 221         mTarget = dynamic_cast<SceneObject*>(pObject);
 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   dMemset( strikeTextureNames, 0, sizeof( strikeTextureNames ) );
 245   dMemset( strikeTextures, 0, sizeof( strikeTextures ) );
 246   dMemset( thunderSounds, 0, sizeof( thunderSounds ) );
 247}
 248
 249LightningData::~LightningData()
 250{
 251
 252}
 253
 254//--------------------------------------------------------------------------
 255void LightningData::initPersistFields()
 256{
 257   addField( "strikeSound", TYPEID< SFXTrack >(), Offset(strikeSound, LightningData),
 258      "Sound profile to play when a lightning strike occurs." );
 259   addField( "thunderSounds", TYPEID< SFXTrack >(), Offset(thunderSounds, LightningData), MaxThunders,
 260      "@brief List of thunder sound effects to play.\n\n"
 261      "A random one of these sounds will be played shortly after each strike "
 262      "occurs." );
 263   addField( "strikeTextures", TypeString, Offset(strikeTextureNames, LightningData), MaxTextures,
 264      "List of textures to use to render lightning strikes." );
 265
 266   Parent::initPersistFields();
 267}
 268
 269
 270//--------------------------------------------------------------------------
 271bool LightningData::onAdd()
 272{
 273   if(!Parent::onAdd())
 274      return false;
 275
 276   return true;
 277}
 278
 279
 280bool LightningData::preload(bool server, String &errorStr)
 281{
 282   if (Parent::preload(server, errorStr) == false)
 283      return false;
 284
 285   dQsort(thunderSounds, MaxThunders, sizeof(SFXTrack*), cmpSounds);
 286   for (numThunders = 0; numThunders < MaxThunders && thunderSounds[numThunders] != NULL; numThunders++) {
 287      //
 288   }
 289
 290   if (server == false) 
 291   {
 292      String sfxErrorStr;
 293      for (U32 i = 0; i < MaxThunders; i++) {
 294         if( !sfxResolve( &thunderSounds[ i ], sfxErrorStr ) )
 295            Con::errorf(ConsoleLogEntry::General, "LightningData::preload: Invalid packet: %s", sfxErrorStr.c_str());
 296      }
 297
 298      if( !sfxResolve( &strikeSound, sfxErrorStr ) )
 299         Con::errorf(ConsoleLogEntry::General, "LightningData::preload: Invalid packet: %s", sfxErrorStr.c_str());
 300
 301      for (U32 i = 0; i < MaxTextures; i++) 
 302      {
 303         if (strikeTextureNames[i][0])
 304            strikeTextures[i] = GFXTexHandle(strikeTextureNames[i], &GFXDefaultStaticDiffuseProfile, avar("%s() - strikeTextures[%d] (line %d)", __FUNCTION__, i, __LINE__));
 305      }
 306   }
 307
 308
 309   return true;
 310}
 311
 312
 313//--------------------------------------------------------------------------
 314void LightningData::packData(BitStream* stream)
 315{
 316   Parent::packData(stream);
 317
 318   U32 i;
 319   for (i = 0; i < MaxThunders; i++)
 320      sfxWrite( stream, thunderSounds[ i ] );
 321   for (i = 0; i < MaxTextures; i++) {
 322      stream->writeString(strikeTextureNames[i]);
 323   }
 324
 325   sfxWrite( stream, strikeSound );
 326}
 327
 328void LightningData::unpackData(BitStream* stream)
 329{
 330   Parent::unpackData(stream);
 331
 332   U32 i;
 333   for (i = 0; i < MaxThunders; i++)
 334      sfxRead( stream, &thunderSounds[ i ] );
 335   for (i = 0; i < MaxTextures; i++) {
 336      strikeTextureNames[i] = stream->readSTString();
 337   }
 338
 339   sfxRead( stream, &strikeSound );
 340}
 341
 342
 343//--------------------------------------------------------------------------
 344//--------------------------------------
 345//
 346Lightning::Lightning()
 347{
 348   mNetFlags.set(Ghostable</a>|<a href="/coding/class/classnetobject/#classnetobject_1ab7f7b94af4c238c69f5673a98199a01caa501b2b8ffe184a0a58342a1b9790258">ScopeAlways);
 349   mTypeMask |= StaticObjectType</a>|<a href="/coding/file/objecttypes_8h/#objecttypes_8h_1ac18d4a11e9446825e48fd474d7529084a43891480c40ab2b66f5d17713401a340">EnvironmentObjectType;
 350
 351   mLastThink = 0;
 352
 353   mStrikeListHead  = NULL;
 354   mThunderListHead = NULL;
 355
 356   strikesPerMinute = 12;
 357   strikeWidth = 2.5;
 358   chanceToHitTarget = 0.5f;
 359   strikeRadius = 20.0f;
 360   boltStartRadius = 20.0f;
 361   color.set( 1.0f, 1.0f, 1.0f, 1.0f );
 362   fadeColor.set( 0.1f, 0.1f, 1.0f, 1.0f );
 363   useFog = true;
 364
 365   setScale( VectorF( 512.0f, 512.0f, 300.0f ) );
 366}
 367
 368Lightning::~Lightning()
 369{
 370   while( mThunderListHead )
 371   {
 372      Thunder* next = mThunderListHead->next;
 373      delete mThunderListHead;
 374      mThunderListHead = next;
 375   }
 376
 377   while( mStrikeListHead )
 378   {
 379      Strike* next = mStrikeListHead->next;
 380      delete mStrikeListHead;
 381      mStrikeListHead = next;
 382   }
 383}
 384
 385//--------------------------------------------------------------------------
 386void Lightning::initPersistFields()
 387{
 388   addGroup( "Strikes" );
 389   addField( "strikesPerMinute", TypeS32, Offset(strikesPerMinute, Lightning),
 390      "@brief Number of lightning strikes to perform per minute.\n\n"
 391      "Automatically invokes strikeRandomPoint() at regular intervals." );
 392   addField( "strikeWidth", TypeF32, Offset(strikeWidth, Lightning),
 393      "Width of a lightning bolt." );
 394   addField( "strikeRadius", TypeF32, Offset(strikeRadius, Lightning),
 395      "@brief Horizontal size (XY plane) of the search box used to find and "
 396      "damage Player or Vehicle objects within range of the strike.\n\n"
 397      "Only the object at highest altitude with a clear line of sight to the "
 398      "bolt will be hit." );
 399   endGroup( "Strikes" );
 400
 401   addGroup( "Colors" );
 402   addField( "color", TypeColorF, Offset(color, Lightning),
 403      "Color to blend the strike texture with." );
 404   addField( "fadeColor", TypeColorF, Offset(fadeColor, Lightning),
 405      "@brief Color to blend the strike texture with when the bolt is fading away.\n\n"
 406      "Bolts fade away automatically shortly after the strike occurs." );
 407   endGroup( "Colors" );
 408
 409   addGroup( "Bolts" );
 410   addField( "chanceToHitTarget", TypeF32, Offset(chanceToHitTarget, Lightning),
 411      "Percentage chance (0-1) that a given lightning bolt will hit something." );
 412   addField( "boltStartRadius", TypeF32, Offset(boltStartRadius, Lightning),
 413      "@brief Radial distance from the center of the Lightning object for the "
 414      "start point of the bolt.\n\n"
 415      "The actual start point will be a random point within this radius." );
 416   addField( "useFog", TypeBool, Offset(useFog, Lightning),
 417      "Controls whether lightning bolts are affected by fog when they are rendered." );
 418   endGroup( "Bolts" );
 419
 420   Parent::initPersistFields();
 421}
 422
 423//--------------------------------------------------------------------------
 424bool Lightning::onAdd()
 425{
 426   if(!Parent::onAdd())
 427      return false;
 428
 429   mObjBox.minExtents.set( -0.5f, -0.5f, -0.5f );
 430   mObjBox.maxExtents.set(  0.5f,  0.5f,  0.5f );
 431
 432   resetWorldBox();
 433   addToScene();
 434
 435   return true;
 436}
 437
 438
 439void Lightning::onRemove()
 440{
 441   removeFromScene();
 442
 443   Parent::onRemove();
 444}
 445
 446
 447bool Lightning::onNewDataBlock( GameBaseData *dptr, bool reload )
 448{
 449   mDataBlock = dynamic_cast<LightningData*>( dptr );
 450   if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
 451      return false;
 452
 453   scriptOnNewDataBlock();
 454   return true;
 455}
 456
 457
 458//--------------------------------------------------------------------------
 459void Lightning::prepRenderImage( SceneRenderState* state )
 460{
 461   ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
 462   ri->renderDelegate.bind(this, &Lightning::renderObject);
 463   // The Lightning isn't technically foliage but our debug
 464   // effect seems to render best as a Foliage type (translucent,
 465   // renders itself, no sorting)
 466   ri->type = RenderPassManager::RIT_Foliage;
 467   state->getRenderPass()->addInst( ri );
 468}
 469
 470
 471void Lightning::renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat)
 472{
 473   if (overrideMat)
 474      return;
 475
 476   if (mLightningSB.isNull())
 477   {
 478      GFXStateBlockDesc desc;
 479      desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendOne);
 480      desc.setCullMode(GFXCullNone);
 481      desc.zWriteEnable = false;
 482      desc.samplersDefined = true;
 483      desc.samplers[0].magFilter = GFXTextureFilterLinear;
 484      desc.samplers[0].minFilter = GFXTextureFilterLinear;
 485      desc.samplers[0].addressModeU = GFXAddressWrap;
 486      desc.samplers[0].addressModeV = GFXAddressWrap;
 487
 488      mLightningSB = GFX->createStateBlock(desc);
 489
 490   }
 491
 492   GFX->setStateBlock(mLightningSB);
 493
 494
 495   Strike* walk = mStrikeListHead;
 496   while (walk != NULL)
 497   {
 498      GFX->setTexture(0, mDataBlock->strikeTextures[0]);
 499
 500      for( U32 i=0; i<3; i++ )
 501      {
 502         if( walk->bolt[i].isFading )
 503         {
 504            F32 alpha = 1.0f - walk->bolt[i].percentFade;
 505            if( alpha < 0.0f ) alpha = 0.0f;
 506            PrimBuild::color4f( fadeColor.red, fadeColor.green, fadeColor.blue, alpha );
 507         }
 508         else
 509         {
 510            PrimBuild::color4f( color.red, color.green, color.blue, color.alpha );
 511         }
 512         walk->bolt[i].render( state->getCameraPosition() );
 513      }
 514
 515      walk = walk->next;
 516   }
 517
 518   //GFX->setZWriteEnable(true);
 519   //GFX->setAlphaTestEnable(false);
 520   //GFX->setAlphaBlendEnable(false);
 521}
 522
 523void Lightning::scheduleThunder(Strike* newStrike)
 524{
 525   AssertFatal(isClientObject(), "Lightning::scheduleThunder: server objects should not enter this version of the function");
 526
 527   // If no thunder sounds, don't schedule anything!
 528   if (mDataBlock->numThunders == 0)
 529      return;
 530
 531   GameConnection* connection = GameConnection::getConnectionToServer();
 532   if (connection) {
 533      MatrixF cameraMatrix;
 534
 535      if (connection->getControlCameraTransform(0, &cameraMatrix)) {
 536         Point3F worldPos;
 537         cameraMatrix.getColumn(3, &worldPos);
 538
 539         worldPos.x -= newStrike->xVal;
 540         worldPos.y -= newStrike->yVal;
 541         worldPos.z  = 0.0f;
 542
 543         F32 dist = worldPos.len();
 544         F32 t    = dist / 330.0f;
 545
 546         // Ok, we need to schedule a random strike sound t secs in the future...
 547         //
 548         if (t <= 0.03f) {
 549            // If it's really close, just play it...
 550            U32 thunder = sgLightningRand.randI(0, mDataBlock->numThunders - 1);
 551            SFX->playOnce(mDataBlock->thunderSounds[thunder]);
 552         } else {
 553            Thunder* pThunder = new Thunder;
 554            pThunder->tRemaining = t;
 555            pThunder->next       = mThunderListHead;
 556            mThunderListHead     = pThunder;
 557         }
 558      }
 559   }
 560}
 561
 562
 563//--------------------------------------------------------------------------
 564void Lightning::processTick(const Move* move)
 565{
 566   Parent::processTick(move);
 567
 568   if (isServerObject() && !isHidden()) {
 569      S32 msBetweenStrikes = (S32)(60.0 / strikesPerMinute * 1000.0);
 570
 571      mLastThink += TickMs;
 572      if( mLastThink > msBetweenStrikes )
 573      {
 574         strikeRandomPoint();
 575         mLastThink -= msBetweenStrikes;
 576      }
 577   }
 578}
 579
 580void Lightning::interpolateTick(F32 dt)
 581{
 582   Parent::interpolateTick(dt);
 583}
 584
 585void Lightning::advanceTime(F32 dt)
 586{
 587   Parent::advanceTime(dt);
 588
 589   Strike** pWalker = &mStrikeListHead;
 590   while (*pWalker != NULL) {
 591      Strike* pStrike = *pWalker;
 592
 593      for( U32 i=0; i<3; i++ )
 594      {
 595         pStrike->bolt[i].update( dt );
 596      }
 597
 598      pStrike->currentAge += dt;
 599      if (pStrike->currentAge > pStrike->deathAge) {
 600         *pWalker = pStrike->next;
 601         delete pStrike;
 602      } else {
 603         pWalker = &((*pWalker)->next);
 604      }
 605   }
 606
 607   Thunder** pThunderWalker = &mThunderListHead;
 608   while (*pThunderWalker != NULL) {
 609      Thunder* pThunder = *pThunderWalker;
 610
 611      pThunder->tRemaining -= dt;
 612      if (pThunder->tRemaining <= 0.0f) {
 613         *pThunderWalker = pThunder->next;
 614         delete pThunder;
 615
 616         // Play the sound...
 617         U32 thunder = sgLightningRand.randI(0, mDataBlock->numThunders - 1);
 618         SFX->playOnce(mDataBlock->thunderSounds[thunder]);
 619      } else {
 620         pThunderWalker = &((*pThunderWalker)->next);
 621      }
 622   }
 623}
 624
 625
 626//--------------------------------------------------------------------------
 627void Lightning::processEvent(LightningStrikeEvent* pEvent)
 628{
 629      AssertFatal(pEvent->mStart.x >= 0.0f && pEvent->mStart.x <= 1.0f, "Out of bounds coord!");
 630
 631      Strike* pStrike = new Strike;
 632
 633      Point3F strikePoint;
 634      strikePoint.zero();
 635
 636      if( pEvent->mTarget )
 637      {
 638         Point3F objectCenter;
 639         pEvent->mTarget->getObjBox().getCenter( &objectCenter );
 640         objectCenter.convolve( pEvent->mTarget->getScale() );
 641         pEvent->mTarget->getTransform().mulP( objectCenter );
 642
 643         strikePoint = objectCenter;
 644      }
 645      else
 646      {
 647         strikePoint.x = pEvent->mStart.x;
 648         strikePoint.y = pEvent->mStart.y;
 649         strikePoint *= mObjScale;
 650         strikePoint += getPosition();
 651         strikePoint += Point3F( -mObjScale.x * 0.5f, -mObjScale.y * 0.5f, 0.0f );
 652
 653         RayInfo rayInfo;
 654         Point3F start = strikePoint;
 655         start.z = mObjScale.z * 0.5f + getPosition().z;
 656         strikePoint.z += -mObjScale.z * 0.5f;
 657         bool rayHit = gClientContainer.castRay( start, strikePoint,
 658                                      (STATIC_COLLISION_TYPEMASK | WaterObjectType),
 659                                      &rayInfo);
 660         if( rayHit )
 661         {
 662            strikePoint.z = rayInfo.point.z;
 663         }
 664         else
 665         {
 666            strikePoint.z = pStrike->bolt[0].findHeight( strikePoint, getSceneManager() );
 667         }
 668      }
 669
 670      pStrike->xVal       = strikePoint.x;
 671      pStrike->yVal       = strikePoint.y;
 672
 673      pStrike->deathAge   = 1.6f;
 674      pStrike->currentAge = 0.0f;
 675      pStrike->next       = mStrikeListHead;
 676
 677      for( U32 i=0; i<3; i++ )
 678      {
 679         F32 randStart = boltStartRadius;
 680         F32 height = mObjScale.z * 0.5f + getPosition().z;
 681         pStrike->bolt[i].startPoint.set( pStrike->xVal + gRandGen.randF( -randStart, randStart ), pStrike->yVal + gRandGen.randF( -randStart, randStart ), height );
 682         pStrike->bolt[i].endPoint = strikePoint;
 683         pStrike->bolt[i].width = strikeWidth;
 684         pStrike->bolt[i].numMajorNodes = 10;
 685         pStrike->bolt[i].maxMajorAngle = 30.0f;
 686         pStrike->bolt[i].numMinorNodes = 4;
 687         pStrike->bolt[i].maxMinorAngle = 15.0f;
 688         pStrike->bolt[i].generate();
 689         pStrike->bolt[i].startSplits();
 690         pStrike->bolt[i].lifetime = 1.0f;
 691         pStrike->bolt[i].fadeTime = 0.2f;
 692         pStrike->bolt[i].renderTime = gRandGen.randF(0.0f, 0.25f);
 693      }
 694
 695      mStrikeListHead     = pStrike;
 696
 697      scheduleThunder(pStrike);
 698
 699      MatrixF trans(true);
 700      trans.setPosition( strikePoint );
 701
 702      if (mDataBlock->strikeSound)
 703      {
 704         SFX->playOnce(mDataBlock->strikeSound, &trans );
 705      }
 706
 707}
 708
 709void Lightning::warningFlashes()
 710{
 711   AssertFatal(isServerObject(), "Error, client objects may not initiate lightning!");
 712
 713
 714   SimGroup* pClientGroup = Sim::getClientGroup();
 715   for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++) {
 716      NetConnection* nc = static_cast<NetConnection*>(*itr);
 717      if (nc != NULL)
 718      {
 719         LightningStrikeEvent* pEvent = new LightningStrikeEvent;
 720         pEvent->mLightning = this;
 721
 722         nc->postNetEvent(pEvent);
 723      }
 724   }
 725}
 726
 727void Lightning::strikeRandomPoint()
 728{
 729   AssertFatal(isServerObject(), "Error, client objects may not initiate lightning!");
 730
 731
 732   Point3F strikePoint( gRandGen.randF( 0.0f, 1.0f ), gRandGen.randF( 0.0f, 1.0f ), 0.0f );
 733
 734   // check if an object is within target range
 735
 736   strikePoint *= mObjScale;
 737   strikePoint += getPosition();
 738   strikePoint += Point3F( -mObjScale.x * 0.5f, -mObjScale.y * 0.5f, 0.0f );
 739
 740   Box3F queryBox;
 741   F32 boxWidth = strikeRadius * 2.0f;
 742
 743   queryBox.minExtents.set( -boxWidth * 0.5f, -boxWidth * 0.5f, -mObjScale.z * 0.5f );
 744   queryBox.maxExtents.set(  boxWidth * 0.5f,  boxWidth * 0.5f,  mObjScale.z * 0.5f );
 745   queryBox.minExtents += strikePoint;
 746   queryBox.maxExtents += strikePoint;
 747
 748   SimpleQueryList sql;
 749   getContainer()->findObjects(queryBox, DAMAGEABLE_TYPEMASK,
 750                               SimpleQueryList::insertionCallback, &sql);
 751
 752   SceneObject *highestObj = NULL;
 753   F32 highestPnt = 0.0f;
 754
 755   for( U32 i = 0; i < sql.mList.size(); i++ )
 756   {
 757      Point3F objectCenter;
 758      sql.mList[i]->getObjBox().getCenter(&objectCenter);
 759      objectCenter.convolve(sql.mList[i]->getScale());
 760      sql.mList[i]->getTransform().mulP(objectCenter);
 761
 762      // check if object can be struck
 763
 764      RayInfo rayInfo;
 765      Point3F start = objectCenter;
 766      start.z = mObjScale.z * 0.5f + getPosition().z;
 767      Point3F end = objectCenter;
 768      end.z = -mObjScale.z * 0.5f + getPosition().z;
 769      bool rayHit = gServerContainer.castRay( start, end,
 770                                   (0xFFFFFFFF),
 771                                   &rayInfo);
 772
 773      if( rayHit && rayInfo.object == sql.mList[i] )
 774      {
 775         if( !highestObj )
 776         {
 777            highestObj = sql.mList[i];
 778            highestPnt = objectCenter.z;
 779            continue;
 780         }
 781
 782         if( objectCenter.z > highestPnt )
 783         {
 784            highestObj = sql.mList[i];
 785            highestPnt = objectCenter.z;
 786         }
 787      }
 788
 789
 790   }
 791
 792   // hah haaaaa, we have a target!
 793   SceneObject *targetObj = NULL;
 794   if( highestObj )
 795   {
 796      F32 chance = gRandGen.randF();
 797      if( chance <= chanceToHitTarget )
 798      {
 799         Point3F objectCenter;
 800         highestObj->getObjBox().getCenter(&objectCenter);
 801         objectCenter.convolve(highestObj->getScale());
 802         highestObj->getTransform().mulP(objectCenter);
 803
 804         bool playerInWarmup = false;
 805         Player *playerObj = dynamic_cast< Player * >(highestObj);
 806         if( playerObj )
 807         {
 808            if( !playerObj->getControllingClient() )
 809            {
 810               playerInWarmup = true;
 811            }
 812         }
 813
 814         if( !playerInWarmup )
 815         {
 816            applyDamage_callback( objectCenter, VectorF( 0.0f, 0.0f, 1.0f ), highestObj );
 817            targetObj = highestObj;
 818         }
 819      }
 820   }
 821
 822   SimGroup* pClientGroup = Sim::getClientGroup();
 823   for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++)
 824   {
 825      NetConnection* nc = static_cast<NetConnection*>(*itr);
 826
 827      LightningStrikeEvent* pEvent = new LightningStrikeEvent;
 828      pEvent->mLightning = this;
 829
 830      pEvent->mStart.x = strikePoint.x;
 831      pEvent->mStart.y = strikePoint.y;
 832      pEvent->mTarget = targetObj;
 833
 834      nc->postNetEvent(pEvent);
 835   }
 836
 837
 838}
 839
 840//--------------------------------------------------------------------------
 841void Lightning::strikeObject(ShapeBase*)
 842{
 843   AssertFatal(isServerObject(), "Error, client objects may not initiate lightning!");
 844
 845   AssertFatal(false, "Lightning::strikeObject is not implemented.");
 846}
 847
 848
 849//--------------------------------------------------------------------------
 850U32 Lightning::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
 851{
 852   U32 retMask = Parent::packUpdate(con, mask, stream);
 853
 854   // Only write data if this is the initial packet or we've been inspected.
 855   if (stream->writeFlag(mask & (InitialUpdateMask | ExtendedInfoMask)))
 856   {
 857      // Initial update
 858      mathWrite(*stream, getPosition());
 859      mathWrite(*stream, mObjScale);
 860
 861      stream->write(strikeWidth);
 862      stream->write(chanceToHitTarget);
 863      stream->write(strikeRadius);
 864      stream->write(boltStartRadius);
 865      stream->write(color.red);
 866      stream->write(color.green);
 867      stream->write(color.blue);
 868      stream->write(fadeColor.red);
 869      stream->write(fadeColor.green);
 870      stream->write(fadeColor.blue);
 871      stream->write(useFog);
 872      stream->write(strikesPerMinute);
 873   }
 874
 875   return retMask;
 876}
 877
 878//--------------------------------------------------------------------------
 879void Lightning::unpackUpdate(NetConnection* con, BitStream* stream)
 880{
 881   Parent::unpackUpdate(con, stream);
 882
 883   if (stream->readFlag())
 884   {
 885      // Initial update
 886      Point3F pos;
 887      mathRead(*stream, &pos);
 888      setPosition( pos );
 889
 890      mathRead(*stream, &mObjScale);
 891
 892      stream->read(&strikeWidth);
 893      stream->read(&chanceToHitTarget);
 894      stream->read(&strikeRadius);
 895      stream->read(&boltStartRadius);
 896      stream->read(&color.red);
 897      stream->read(&color.green);
 898      stream->read(&color.blue);
 899      stream->read(&fadeColor.red);
 900      stream->read(&fadeColor.green);
 901      stream->read(&fadeColor.blue);
 902      stream->read(&useFog);
 903      stream->read(&strikesPerMinute);
 904   }
 905}
 906
 907//--------------------------------------------------------------------------
 908
 909DefineEngineMethod(Lightning, warningFlashes, void, (),,
 910   "@brief Creates a LightningStrikeEvent that triggers harmless lightning "
 911   "bolts on all clients.\n"
 912   "No objects will be damaged by these bolts.\n"
 913   "@tsexample\n"
 914   "// Generate a harmless lightning strike effect on all clients\n"
 915   "%lightning.warningFlashes();\n"
 916   "@endtsexample" )
 917{
 918   if (object->isServerObject()) 
 919      object->warningFlashes();
 920}
 921
 922DefineEngineMethod(Lightning, strikeRandomPoint, void, (),,
 923   "Creates a LightningStrikeEvent which attempts to strike and damage a random "
 924   "object in range of the Lightning object.\n"
 925   "@tsexample\n"
 926   "// Generate a damaging lightning strike effect on all clients\n"
 927   "%lightning.strikeRandomPoint();\n"
 928   "@endtsexample" )
 929{
 930   if (object->isServerObject()) 
 931      object->strikeRandomPoint();
 932}
 933
 934DefineEngineMethod(Lightning, strikeObject, void, (ShapeBase* pSB),,
 935   "Creates a LightningStrikeEvent which strikes a specific object.\n"
 936   "@note This method is currently unimplemented.\n" )
 937{
 938      object->strikeObject(pSB);
 939}
 940
 941//**************************************************************************
 942// Lightning Bolt
 943//**************************************************************************
 944LightningBolt::LightningBolt()
 945{
 946   width = 0.1f;
 947   startPoint.zero();
 948   endPoint.zero();
 949   chanceOfSplit = 0.0f;
 950   isFading = false;
 951   elapsedTime = 0.0f;
 952   lifetime = 1.0f;
 953   startRender = false;
 954}
 955
 956//--------------------------------------------------------------------------
 957// Destructor
 958//--------------------------------------------------------------------------
 959LightningBolt::~LightningBolt()
 960{
 961   splitList.clear();
 962}
 963
 964//--------------------------------------------------------------------------
 965// Generate nodes
 966//--------------------------------------------------------------------------
 967void LightningBolt::NodeManager::generateNodes()
 968{
 969   F32 overallDist = VectorF( endPoint - startPoint ).magnitudeSafe();
 970   F32 minDistBetweenNodes = overallDist / (numNodes-1);
 971   F32 maxDistBetweenNodes = minDistBetweenNodes / mCos( maxAngle * M_PI_F / 180.0f );
 972
 973   VectorF mainLineDir = endPoint - startPoint;
 974   mainLineDir.normalizeSafe();
 975
 976   for( U32 i=0; i<numNodes; i++ )
 977   {
 978      Node node;
 979
 980      if( i == 0 )
 981      {
 982         node.point = startPoint;
 983         node.dirToMainLine = mainLineDir;
 984         nodeList[i] = node;
 985         continue;
 986      }
 987      if( i == numNodes - 1 )
 988      {
 989         node.point = endPoint;
 990         nodeList[i] = node;
 991         break;
 992      }
 993
 994      Node lastNode = nodeList[i-1];
 995
 996      F32 segmentLength = gRandGen.randF( minDistBetweenNodes, maxDistBetweenNodes );
 997      VectorF segmentDir = MathUtils::randomDir( lastNode.dirToMainLine, 0, maxAngle );
 998      node.point = lastNode.point + segmentDir * segmentLength;
 999
1000      node.dirToMainLine = endPoint - node.point;
1001      node.dirToMainLine.normalizeSafe();
1002      nodeList[i] = node;
1003   }
1004}
1005
1006
1007//--------------------------------------------------------------------------
1008// Render bolt
1009//--------------------------------------------------------------------------
1010void LightningBolt::render( const Point3F &camPos )
1011{
1012   if (!startRender)
1013      return;
1014
1015   if (!isFading)
1016      generateMinorNodes();
1017
1018   U32 maxVerts = 0;
1019   for (U32 i = 0; i < mMinorNodes.size(); i++)
1020      maxVerts += mMinorNodes[i].numNodes * 2;
1021
1022   PrimBuild::begin(GFXTriangleStrip, maxVerts);
1023
1024   for (U32 i = 0; i < mMinorNodes.size(); i++)
1025   {
1026      if (i+1 == mMinorNodes.size())
1027         renderSegment(mMinorNodes[i], camPos, true);
1028      else
1029         renderSegment(mMinorNodes[i], camPos, false);
1030   }
1031
1032   PrimBuild::end();
1033
1034   for(LightingBoltList::Iterator i = splitList.begin(); i != splitList.end(); ++i)
1035   {
1036      if( isFading )
1037      {
1038         i->isFading = true;
1039      }
1040      i->render( camPos );
1041   }
1042
1043}
1044
1045//--------------------------------------------------------------------------
1046// Render segment
1047//--------------------------------------------------------------------------
1048void LightningBolt::renderSegment( NodeManager &segment, const Point3F &camPos, bool renderLastPoint )
1049{
1050
1051   for (U32 i = 0; i < segment.numNodes; i++)
1052   {
1053      Point3F  curPoint = segment.nodeList[i].point;
1054
1055      Point3F  nextPoint;
1056      Point3F  segDir;
1057
1058      if( i == (segment.numNodes-1) )
1059      {
1060         if( renderLastPoint )
1061         {
1062            segDir = curPoint - segment.nodeList[i-1].point;
1063         }
1064         else
1065         {
1066            continue;
1067         }
1068      }
1069      else
1070      {
1071         nextPoint = segment.nodeList[i+1].point;
1072         segDir = nextPoint - curPoint;
1073      }
1074      segDir.normalizeSafe();
1075
1076
1077      Point3F dirFromCam = curPoint - camPos;
1078      Point3F crossVec;
1079      mCross(dirFromCam, segDir, &crossVec);
1080      crossVec.normalize();
1081      crossVec *= width * 0.5f;
1082
1083      F32 u = i % 2;
1084
1085      PrimBuild::texCoord2f( u, 1.0 );
1086      PrimBuild::vertex3fv( curPoint - crossVec );
1087
1088      PrimBuild::texCoord2f( u, 0.0 );
1089      PrimBuild::vertex3fv( curPoint + crossVec );
1090   }
1091
1092}
1093
1094//----------------------------------------------------------------------------
1095// Find height
1096//----------------------------------------------------------------------------
1097F32 LightningBolt::findHeight( Point3F &point, SceneManager *sceneManager )
1098{
1099   const Vector< SceneObject*> terrains = sceneManager->getContainer()->getTerrains();
1100   for( Vector< SceneObject* >::const_iterator iter = terrains.begin(); iter != terrains.end(); ++ iter )
1101   {
1102      TerrainBlock* terrain = dynamic_cast< TerrainBlock* >( *iter );
1103      if( !terrain )
1104         continue;
1105
1106      Point3F terrPt = point;
1107      terrain->getWorldTransform().mulP(terrPt);
1108
1109      F32 h;
1110      if( terrain->getHeight( Point2F( terrPt.x, terrPt.y ), &h ) )
1111         return h;
1112   }
1113
1114   return 0.f;
1115}
1116
1117
1118//----------------------------------------------------------------------------
1119// Generate lightning bolt
1120//----------------------------------------------------------------------------
1121void LightningBolt::generate()
1122{
1123   mMajorNodes.startPoint   = startPoint;
1124   mMajorNodes.endPoint     = endPoint;
1125   mMajorNodes.numNodes     = numMajorNodes;
1126   mMajorNodes.maxAngle     = maxMajorAngle;
1127
1128   mMajorNodes.generateNodes();
1129
1130   generateMinorNodes();
1131
1132}
1133
1134//----------------------------------------------------------------------------
1135// Generate Minor Nodes
1136//----------------------------------------------------------------------------
1137void LightningBolt::generateMinorNodes()
1138{
1139   mMinorNodes.clear();
1140
1141   for( S32 i=0; i<mMajorNodes.numNodes - 1; i++ )
1142   {
1143      NodeManager segment;
1144      segment.startPoint = mMajorNodes.nodeList[i].point;
1145      segment.endPoint = mMajorNodes.nodeList[i+1].point;
1146      segment.numNodes = numMinorNodes;
1147      segment.maxAngle = maxMinorAngle;
1148      segment.generateNodes();
1149
1150      mMinorNodes.increment(1);
1151      mMinorNodes[i] = segment;
1152   }
1153}
1154
1155//----------------------------------------------------------------------------
1156// Recursive algo to create bolts that split off from main bolt
1157//----------------------------------------------------------------------------
1158void LightningBolt::createSplit( const Point3F &startPoint, const Point3F &endPoint, U32 depth, F32 width )
1159{
1160   if( depth == 0 )
1161      return;
1162     
1163   F32 chanceToEnd = gRandGen.randF();
1164   if( chanceToEnd > 0.70f )
1165      return;
1166
1167   if( width < 0.75f )
1168      width = 0.75f;
1169
1170   VectorF diff = endPoint - startPoint;
1171   F32 length = diff.len();
1172   diff.normalizeSafe();
1173
1174   LightningBolt newBolt;
1175   newBolt.startPoint = startPoint;
1176   newBolt.endPoint = endPoint;
1177   newBolt.width = width;
1178   newBolt.numMajorNodes = 3;
1179   newBolt.maxMajorAngle = 30.0f;
1180   newBolt.numMinorNodes = 3;
1181   newBolt.maxMinorAngle = 10.0f;
1182   newBolt.startRender = true;
1183   newBolt.generate();
1184
1185   splitList.pushBack( newBolt );
1186
1187   VectorF newDir1 = MathUtils::randomDir( diff, 10.0f, 45.0f );
1188   Point3F newEndPoint1 = endPoint + newDir1 * gRandGen.randF( 0.5f, 1.5f ) * length;
1189
1190   VectorF newDir2 = MathUtils::randomDir( diff, 10.0f, 45.0f );
1191   Point3F newEndPoint2 = endPoint + newDir2 * gRandGen.randF( 0.5f, 1.5f ) * length;
1192
1193   createSplit( endPoint, newEndPoint1, depth - 1, width * 0.30f );
1194   createSplit( endPoint, newEndPoint2, depth - 1, width * 0.30f );
1195
1196}
1197
1198//----------------------------------------------------------------------------
1199// Start split - kick off the recursive 'createSplit' procedure
1200//----------------------------------------------------------------------------
1201void LightningBolt::startSplits()
1202{
1203
1204   for( U32 i=0; i<mMajorNodes.numNodes-1; i++ )
1205   {
1206      if( gRandGen.randF() > 0.3f )
1207        continue;
1208
1209      Node node = mMajorNodes.nodeList[i];
1210      Node node2 = mMajorNodes.nodeList[i+1];
1211
1212      VectorF segDir = node2.point - node.point;
1213      F32 length = segDir.len();
1214      segDir.normalizeSafe();
1215
1216      VectorF newDir = MathUtils::randomDir( segDir, 20.0f, 40.0f );
1217      Point3F newEndPoint = node.point + newDir * gRandGen.randF( 0.5f, 1.5f ) * length;
1218
1219
1220      createSplit( node.point, newEndPoint, 4, width * 0.30f );
1221   }
1222
1223
1224}
1225
1226//----------------------------------------------------------------------------
1227// Update
1228//----------------------------------------------------------------------------
1229void LightningBolt::update( F32 dt )
1230{
1231   elapsedTime += dt;
1232
1233   F32 percentDone = elapsedTime / lifetime;
1234
1235   if( elapsedTime > fadeTime )
1236   {
1237      isFading = true;
1238      percentFade = percentDone + (fadeTime/<a href="/coding/class/structlightningbolt/#structlightningbolt_1af344b46100a4d07c2260a37e25e98bbe">lifetime</a>);
1239   }
1240
1241   if( elapsedTime > renderTime && !startRender )
1242   {
1243      startRender = true;
1244      isFading = false;
1245      elapsedTime = 0.0f;
1246   }
1247}
1248