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