lightning.cpp
Engine/source/T3D/fx/lightning.cpp
Classes:
class
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
