Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

396 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements a brush model entity that moves along a linear path.
  4. // Water whose level can be changed is implemented using the same entity.
  5. //
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "func_movelinear.h"
  9. #include "entitylist.h"
  10. #include "locksounds.h"
  11. #include "ndebugoverlay.h"
  12. #include "engine/IEngineSound.h"
  13. #include "physics_saverestore.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. // -------------------------------
  17. // SPAWN_FLAGS
  18. // -------------------------------
  19. #define SF_MOVELINEAR_NOTSOLID 8
  20. LINK_ENTITY_TO_CLASS( func_movelinear, CFuncMoveLinear );
  21. LINK_ENTITY_TO_CLASS( momentary_door, CFuncMoveLinear ); // For backward compatibility
  22. //
  23. // func_water_analog is implemented as a linear mover so we can raise/lower the water level.
  24. //
  25. LINK_ENTITY_TO_CLASS( func_water_analog, CFuncMoveLinear );
  26. BEGIN_DATADESC( CFuncMoveLinear )
  27. DEFINE_KEYFIELD( m_vecMoveDir, FIELD_VECTOR, "movedir" ),
  28. DEFINE_KEYFIELD( m_soundStart, FIELD_SOUNDNAME, "StartSound" ),
  29. DEFINE_KEYFIELD( m_soundStop, FIELD_SOUNDNAME, "StopSound" ),
  30. DEFINE_FIELD( m_currentSound, FIELD_SOUNDNAME ),
  31. DEFINE_KEYFIELD( m_flBlockDamage, FIELD_FLOAT, "BlockDamage"),
  32. DEFINE_KEYFIELD( m_flStartPosition, FIELD_FLOAT, "StartPosition"),
  33. DEFINE_KEYFIELD( m_flMoveDistance, FIELD_FLOAT, "MoveDistance"),
  34. // DEFINE_PHYSPTR( m_pFluidController ),
  35. // Inputs
  36. DEFINE_INPUTFUNC( FIELD_VOID, "Open", InputOpen ),
  37. DEFINE_INPUTFUNC( FIELD_VOID, "Close", InputClose ),
  38. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPosition", InputSetPosition ),
  39. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpeed", InputSetSpeed ),
  40. // Outputs
  41. DEFINE_OUTPUT( m_OnFullyOpen, "OnFullyOpen" ),
  42. DEFINE_OUTPUT( m_OnFullyClosed, "OnFullyClosed" ),
  43. // Functions
  44. DEFINE_FUNCTION( StopMoveSound ),
  45. END_DATADESC()
  46. //------------------------------------------------------------------------------
  47. // Purpose: Called before spawning, after keyvalues have been parsed.
  48. //------------------------------------------------------------------------------
  49. void CFuncMoveLinear::Spawn( void )
  50. {
  51. // Convert movedir from angles to a vector
  52. QAngle angMoveDir = QAngle( m_vecMoveDir.x, m_vecMoveDir.y, m_vecMoveDir.z );
  53. AngleVectors( angMoveDir, &m_vecMoveDir );
  54. SetMoveType( MOVETYPE_PUSH );
  55. SetModel( STRING( GetModelName() ) );
  56. // Don't allow zero or negative speeds
  57. if (m_flSpeed <= 0)
  58. {
  59. m_flSpeed = 100;
  60. }
  61. // If move distance is set to zero, use with width of the
  62. // brush to determine the size of the move distance
  63. if (m_flMoveDistance <= 0)
  64. {
  65. Vector vecOBB = CollisionProp()->OBBSize();
  66. vecOBB -= Vector( 2, 2, 2 );
  67. m_flMoveDistance = DotProductAbs( m_vecMoveDir, vecOBB ) - m_flLip;
  68. }
  69. m_vecPosition1 = GetAbsOrigin() - (m_vecMoveDir * m_flMoveDistance * m_flStartPosition);
  70. m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * m_flMoveDistance);
  71. m_vecFinalDest = GetAbsOrigin();
  72. SetTouch( NULL );
  73. Precache();
  74. // It is solid?
  75. SetSolid( SOLID_VPHYSICS );
  76. if ( FClassnameIs( this, "func_water_analog" ) )
  77. {
  78. AddSolidFlags( FSOLID_VOLUME_CONTENTS );
  79. }
  80. if ( !FClassnameIs( this, "func_water_analog" ) && FBitSet (m_spawnflags, SF_MOVELINEAR_NOTSOLID) )
  81. {
  82. AddSolidFlags( FSOLID_NOT_SOLID );
  83. }
  84. CreateVPhysics();
  85. }
  86. bool CFuncMoveLinear::ShouldSavePhysics( void )
  87. {
  88. // don't save physics for func_water_analog, regen
  89. return !FClassnameIs( this, "func_water_analog" );
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Purpose:
  93. //-----------------------------------------------------------------------------
  94. bool CFuncMoveLinear::CreateVPhysics( void )
  95. {
  96. if ( !FClassnameIs( this, "func_water_analog" ) )
  97. {
  98. //normal door
  99. if ( !IsSolidFlagSet( FSOLID_NOT_SOLID ) )
  100. {
  101. VPhysicsInitShadow( false, false );
  102. }
  103. }
  104. else
  105. {
  106. // special contents
  107. AddSolidFlags( FSOLID_VOLUME_CONTENTS );
  108. //SETBITS( m_spawnflags, SF_DOOR_SILENT ); // water is silent for now
  109. IPhysicsObject *pPhysics = VPhysicsInitShadow( false, false );
  110. fluidparams_t fluid;
  111. Assert( CollisionProp()->GetCollisionAngles() == vec3_angle );
  112. fluid.damping = 0.01f;
  113. fluid.surfacePlane[0] = 0;
  114. fluid.surfacePlane[1] = 0;
  115. fluid.surfacePlane[2] = 1;
  116. fluid.surfacePlane[3] = CollisionProp()->GetCollisionOrigin().z + CollisionProp()->OBBMaxs().z - 1;
  117. fluid.currentVelocity.Init(0,0,0);
  118. fluid.torqueFactor = 0.1f;
  119. fluid.viscosityFactor = 0.01f;
  120. fluid.pGameData = static_cast<void *>(this);
  121. //FIXME: Currently there's no way to specify that you want slime
  122. fluid.contents = CONTENTS_WATER;
  123. m_pFluidController = physenv->CreateFluidController( pPhysics, &fluid );
  124. }
  125. return true;
  126. }
  127. //------------------------------------------------------------------------------
  128. // Purpose:
  129. //------------------------------------------------------------------------------
  130. void CFuncMoveLinear::Precache( void )
  131. {
  132. if (m_soundStart != NULL_STRING)
  133. {
  134. PrecacheScriptSound( (char *) STRING(m_soundStart) );
  135. }
  136. if (m_soundStop != NULL_STRING)
  137. {
  138. PrecacheScriptSound( (char *) STRING(m_soundStop) );
  139. }
  140. m_currentSound = NULL_STRING;
  141. }
  142. //------------------------------------------------------------------------------
  143. // Purpose:
  144. //------------------------------------------------------------------------------
  145. void CFuncMoveLinear::MoveTo(Vector vPosition, float flSpeed)
  146. {
  147. if ( flSpeed != 0 )
  148. {
  149. if ( m_soundStart != NULL_STRING )
  150. {
  151. if (m_currentSound == m_soundStart)
  152. {
  153. StopSound(entindex(), CHAN_BODY, (char*)STRING(m_soundStop));
  154. }
  155. else
  156. {
  157. m_currentSound = m_soundStart;
  158. CPASAttenuationFilter filter( this );
  159. EmitSound_t ep;
  160. ep.m_nChannel = CHAN_BODY;
  161. ep.m_pSoundName = (char*)STRING(m_soundStart);
  162. ep.m_flVolume = 1;
  163. ep.m_SoundLevel = SNDLVL_NORM;
  164. EmitSound( filter, entindex(), ep );
  165. }
  166. }
  167. LinearMove( vPosition, flSpeed );
  168. if ( m_pFluidController )
  169. {
  170. m_pFluidController->WakeAllSleepingObjects();
  171. }
  172. // Clear think (that stops sounds)
  173. SetThink(NULL);
  174. }
  175. }
  176. //------------------------------------------------------------------------------
  177. // Purpose:
  178. //------------------------------------------------------------------------------
  179. void CFuncMoveLinear::StopMoveSound( void )
  180. {
  181. if ( m_soundStart != NULL_STRING && ( m_currentSound == m_soundStart ) )
  182. {
  183. StopSound(entindex(), CHAN_BODY, (char*)STRING(m_soundStart) );
  184. }
  185. if ( m_soundStop != NULL_STRING && ( m_currentSound != m_soundStop ) )
  186. {
  187. m_currentSound = m_soundStop;
  188. CPASAttenuationFilter filter( this );
  189. EmitSound_t ep;
  190. ep.m_nChannel = CHAN_BODY;
  191. ep.m_pSoundName = (char*)STRING(m_soundStop);
  192. ep.m_flVolume = 1;
  193. ep.m_SoundLevel = SNDLVL_NORM;
  194. EmitSound( filter, entindex(), ep );
  195. }
  196. SetThink(NULL);
  197. }
  198. //------------------------------------------------------------------------------
  199. // Purpose:
  200. //------------------------------------------------------------------------------
  201. void CFuncMoveLinear::MoveDone( void )
  202. {
  203. // Stop sounds at the next think, rather than here as another
  204. // SetPosition call might immediately follow the end of this move
  205. SetThink(&CFuncMoveLinear::StopMoveSound);
  206. SetNextThink( gpGlobals->curtime + 0.1f );
  207. BaseClass::MoveDone();
  208. if ( GetAbsOrigin() == m_vecPosition2 )
  209. {
  210. m_OnFullyOpen.FireOutput( this, this );
  211. }
  212. else if ( GetAbsOrigin() == m_vecPosition1 )
  213. {
  214. m_OnFullyClosed.FireOutput( this, this );
  215. }
  216. }
  217. //------------------------------------------------------------------------------
  218. // Purpose:
  219. //------------------------------------------------------------------------------
  220. void CFuncMoveLinear::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  221. {
  222. if ( useType != USE_SET ) // Momentary buttons will pass down a float in here
  223. return;
  224. if ( value > 1.0 )
  225. value = 1.0;
  226. Vector move = m_vecPosition1 + (value * (m_vecPosition2 - m_vecPosition1));
  227. Vector delta = move - GetLocalOrigin();
  228. float speed = delta.Length() * 10;
  229. MoveTo(move, speed);
  230. }
  231. //-----------------------------------------------------------------------------
  232. // Purpose: Sets the position as a value from [0..1].
  233. //-----------------------------------------------------------------------------
  234. void CFuncMoveLinear::SetPosition( float flPosition )
  235. {
  236. Vector vTargetPos = m_vecPosition1 + ( flPosition * (m_vecPosition2 - m_vecPosition1));
  237. if ((vTargetPos - GetLocalOrigin()).Length() > 0.001)
  238. {
  239. MoveTo(vTargetPos, m_flSpeed);
  240. }
  241. }
  242. //------------------------------------------------------------------------------
  243. // Purpose:
  244. //------------------------------------------------------------------------------
  245. void CFuncMoveLinear::InputOpen( inputdata_t &inputdata )
  246. {
  247. if (GetLocalOrigin() != m_vecPosition2)
  248. {
  249. MoveTo(m_vecPosition2, m_flSpeed);
  250. }
  251. }
  252. //------------------------------------------------------------------------------
  253. // Purpose:
  254. //------------------------------------------------------------------------------
  255. void CFuncMoveLinear::InputClose( inputdata_t &inputdata )
  256. {
  257. if (GetLocalOrigin() != m_vecPosition1)
  258. {
  259. MoveTo(m_vecPosition1, m_flSpeed);
  260. }
  261. }
  262. //------------------------------------------------------------------------------
  263. // Purpose: Input handler for setting the position from [0..1].
  264. // Input : Float position.
  265. //-----------------------------------------------------------------------------
  266. void CFuncMoveLinear::InputSetPosition( inputdata_t &inputdata )
  267. {
  268. SetPosition( inputdata.value.Float() );
  269. }
  270. //-----------------------------------------------------------------------------
  271. // Purpose: Called every frame when the bruch is blocked while moving
  272. // Input : pOther - The blocking entity.
  273. //-----------------------------------------------------------------------------
  274. void CFuncMoveLinear::Blocked( CBaseEntity *pOther )
  275. {
  276. // Hurt the blocker
  277. if ( m_flBlockDamage )
  278. {
  279. if ( pOther->m_takedamage == DAMAGE_EVENTS_ONLY )
  280. {
  281. if ( FClassnameIs( pOther, "gib" ) )
  282. UTIL_Remove( pOther );
  283. }
  284. else
  285. pOther->TakeDamage( CTakeDamageInfo( this, this, m_flBlockDamage, DMG_CRUSH ) );
  286. }
  287. }
  288. //-----------------------------------------------------------------------------
  289. // Purpose:
  290. // Input : &inputdata -
  291. //-----------------------------------------------------------------------------
  292. void CFuncMoveLinear::InputSetSpeed( inputdata_t &inputdata )
  293. {
  294. // Set the new speed
  295. m_flSpeed = inputdata.value.Float();
  296. // FIXME: This is a little questionable. Do we want to fix the speed, or let it continue on at the old speed?
  297. float flDistToGoalSqr = ( m_vecFinalDest - GetAbsOrigin() ).LengthSqr();
  298. if ( flDistToGoalSqr > Square( FLT_EPSILON ) )
  299. {
  300. // NOTE: We do NOT want to call sound functions here, just vanilla position changes
  301. LinearMove( m_vecFinalDest, m_flSpeed );
  302. }
  303. }
  304. //-----------------------------------------------------------------------------
  305. // Purpose: Draw any debug text overlays
  306. // Output : Current text offset from the top
  307. //-----------------------------------------------------------------------------
  308. int CFuncMoveLinear::DrawDebugTextOverlays(void)
  309. {
  310. int text_offset = BaseClass::DrawDebugTextOverlays();
  311. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  312. {
  313. char tempstr[512];
  314. float flTravelDist = (m_vecPosition1 - m_vecPosition2).Length();
  315. float flCurDist = (m_vecPosition1 - GetLocalOrigin()).Length();
  316. Q_snprintf(tempstr,sizeof(tempstr),"Current Pos: %3.3f",flCurDist/flTravelDist);
  317. EntityText(text_offset,tempstr,0);
  318. text_offset++;
  319. float flTargetDist = (m_vecPosition1 - m_vecFinalDest).Length();
  320. Q_snprintf(tempstr,sizeof(tempstr),"Target Pos: %3.3f",flTargetDist/flTravelDist);
  321. EntityText(text_offset,tempstr,0);
  322. text_offset++;
  323. }
  324. return text_offset;
  325. }