Counter Strike : Global Offensive Source Code
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.

759 lines
20 KiB

  1. // func_elevator.cpp
  2. // Copyright 2007 Turtle Rock Studios, Inc.
  3. #include "cbase.h"
  4. #include "func_elevator.h"
  5. #include "nav.h"
  6. // NOTE: This has to be the last file included!
  7. #include "tier0/memdbgon.h"
  8. ConVar ZombieAirborneElevator( "z_elevator_in_air", "0" );
  9. //--------------------------------------------------------------------------------------------------------
  10. LINK_ENTITY_TO_CLASS( info_elevator_floor, CInfoElevatorFloor );
  11. //--------------------------------------------------------------------------------------------------------
  12. BEGIN_DATADESC( CInfoElevatorFloor )
  13. // Outputs
  14. DEFINE_OUTPUT( m_OnReachedFloor, "OnReachedFloor" ),
  15. END_DATADESC()
  16. //--------------------------------------------------------------------------------------------------------
  17. void CInfoElevatorFloor::OnReachedFloor( CBaseEntity *elevator )
  18. {
  19. m_OnReachedFloor.FireOutput( elevator, elevator );
  20. }
  21. //--------------------------------------------------------------------------------------------------------
  22. LINK_ENTITY_TO_CLASS( func_elevator, CFuncElevator );
  23. //--------------------------------------------------------------------------------------------------------
  24. BEGIN_DATADESC( CFuncElevator )
  25. DEFINE_KEYFIELD( m_topFloorPosition, FIELD_POSITION_VECTOR, "top" ),
  26. DEFINE_KEYFIELD( m_bottomFloorPosition, FIELD_POSITION_VECTOR, "bottom" ),
  27. DEFINE_KEYFIELD( m_maxSpeed, FIELD_FLOAT, "speed"),
  28. DEFINE_KEYFIELD( m_acceleration, FIELD_FLOAT, "acceleration"),
  29. DEFINE_KEYFIELD( m_soundStart, FIELD_SOUNDNAME, "StartSound" ),
  30. DEFINE_KEYFIELD( m_soundStop, FIELD_SOUNDNAME, "StopSound" ),
  31. DEFINE_KEYFIELD( m_soundDisable, FIELD_SOUNDNAME, "DisableSound" ),
  32. DEFINE_FIELD( m_currentSound, FIELD_SOUNDNAME ),
  33. DEFINE_KEYFIELD( m_flBlockDamage, FIELD_FLOAT, "BlockDamage"),
  34. // Inputs
  35. DEFINE_INPUTFUNC( FIELD_STRING, "MoveToFloor", InputMoveToFloor ),
  36. DEFINE_INPUTFUNC( FIELD_STRING, "Disable", InputDisable ),
  37. // Outputs
  38. DEFINE_OUTPUT( m_OnReachedTop, "OnReachedTop" ),
  39. DEFINE_OUTPUT( m_OnReachedBottom, "OnReachedBottom" ),
  40. // Functions
  41. DEFINE_FUNCTION( StopMoveSoundThink ),
  42. // FIXMEL4DTOMAINMERGE
  43. //DEFINE_FUNCTION( AccelerationThink ),
  44. END_DATADESC()
  45. void SendProxy_Origin( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID );
  46. //--------------------------------------------------------------------------------------------------------
  47. IMPLEMENT_SERVERCLASS_ST(CFuncElevator, DT_FuncElevator)
  48. SendPropExclude( "DT_BaseEntity", "m_vecOrigin" ),
  49. SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_NOSCALE|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ),
  50. SendPropFloat( SENDINFO( m_acceleration ), 0, SPROP_NOSCALE ),
  51. SendPropFloat( SENDINFO( m_currentSpeed ), 0, SPROP_NOSCALE ),
  52. SendPropFloat( SENDINFO( m_movementStartTime ), 0, SPROP_NOSCALE ),
  53. SendPropFloat( SENDINFO( m_movementStartSpeed ), 0, SPROP_NOSCALE ),
  54. SendPropFloat( SENDINFO( m_movementStartZ ), 0, SPROP_NOSCALE ),
  55. SendPropFloat( SENDINFO( m_destinationFloorPosition ), 0, SPROP_NOSCALE ),
  56. SendPropFloat( SENDINFO( m_maxSpeed ), 0, SPROP_NOSCALE ),
  57. SendPropBool( SENDINFO( m_isMoving ) ),
  58. END_SEND_TABLE()
  59. //--------------------------------------------------------------------------------------------------------
  60. CFuncElevator::CFuncElevator()
  61. {
  62. }
  63. //--------------------------------------------------------------------------------------------------------
  64. static int FloorHeightSort( const FloorInfo *a, const FloorInfo *b )
  65. {
  66. return a->height > b->height;
  67. }
  68. //--------------------------------------------------------------------------------------------------------
  69. void CFuncElevator::Spawn( void )
  70. {
  71. SetMoveType( MOVETYPE_CUSTOM );
  72. SetModel( STRING( GetModelName() ) );
  73. SetTouch( NULL );
  74. Precache();
  75. AddFlag( FL_CONVEYOR );
  76. // It is solid?
  77. SetSolid( SOLID_BSP );
  78. if ( m_acceleration == 0.0f )
  79. {
  80. m_acceleration = m_maxSpeed; // 1 second acceleration period
  81. }
  82. m_enabled = true;
  83. // Construct a list of data for each floor
  84. m_floors.RemoveAll();
  85. FloorInfo floor;
  86. floor.button = NULL; // filled in later
  87. floor.height = m_bottomFloorPosition.z;
  88. floor.name = AllocPooledString( "bottom" );
  89. m_floors.AddToHead( floor );
  90. // Floors are specified by CInfoElevatorFloor entities
  91. floor.button = NULL; // filled in later
  92. floor.height = m_topFloorPosition.z;
  93. floor.name = AllocPooledString( "top" );
  94. m_floors.AddToTail( floor );
  95. // Trawl through the list of entities that have map-specified outputs that target this elevator. Grab any that
  96. // are func_buttons that send a MoveToFloor output to us, and save off which floor they go to.
  97. CBaseEntity *inputSource = NULL;
  98. inputSource = gEntList.FindEntityByOutputTarget( NULL, GetEntityName() );
  99. while ( inputSource )
  100. {
  101. datamap_t *dmap = inputSource->GetDataDescMap();
  102. bool found = false;
  103. while ( dmap && !found )
  104. {
  105. int fields = dmap->dataNumFields;
  106. for ( int i = 0; i < fields; i++ )
  107. {
  108. typedescription_t *dataDesc = &dmap->dataDesc[i];
  109. if ( ( dataDesc->fieldType == FIELD_CUSTOM ) && ( dataDesc->flags & FTYPEDESC_OUTPUT ) )
  110. {
  111. CBaseEntityOutput *pOutput = (CBaseEntityOutput *)((int)inputSource + (int)dataDesc->fieldOffset);
  112. const CEventAction *action = pOutput->GetActionForTarget( GetEntityName() );
  113. if ( action )
  114. {
  115. if ( FStrEq( STRING( action->m_iTargetInput ), "MoveToFloor" ) && action->m_iParameter != NULL_STRING )
  116. {
  117. bool isButton = inputSource->ClassMatches( "func_button*" );
  118. bool existingFloor = false;
  119. for ( int n=0; n<m_floors.Count(); ++n )
  120. {
  121. FloorInfo &floor = m_floors[n];
  122. if ( floor.name == action->m_iParameter )
  123. {
  124. if ( isButton )
  125. {
  126. floor.button = inputSource;
  127. }
  128. existingFloor = true;
  129. break;
  130. }
  131. }
  132. if ( !existingFloor )
  133. {
  134. // See if it's a real floor
  135. CBaseEntity *floorEntity = gEntList.FindEntityByName( NULL, action->m_iParameter );
  136. if ( floorEntity )
  137. {
  138. floor.button = (isButton) ? inputSource : NULL;
  139. floor.height = floorEntity->GetAbsOrigin().z;
  140. floor.name = action->m_iParameter;
  141. m_floors.AddToTail( floor );
  142. }
  143. }
  144. found = true;
  145. break;
  146. }
  147. }
  148. }
  149. }
  150. dmap = dmap->baseMap;
  151. }
  152. inputSource = gEntList.FindEntityByOutputTarget( inputSource, GetEntityName() );
  153. }
  154. m_floors.Sort( FloorHeightSort );
  155. m_movementStartTime = gpGlobals->curtime;
  156. m_movementStartSpeed = m_currentSpeed;
  157. m_movementStartZ = GetAbsOrigin().z;
  158. m_destinationFloorPosition = GetAbsOrigin().z;
  159. SetThink(NULL);
  160. m_targetFloor = NULL;
  161. m_accelerationTimer.Start();
  162. m_isMoving = false;
  163. CreateVPhysics();
  164. }
  165. //--------------------------------------------------------------------------------------------------------
  166. bool CFuncElevator::CreateVPhysics( void )
  167. {
  168. VPhysicsInitShadow(false, false);
  169. return true;
  170. }
  171. //--------------------------------------------------------------------------------------------------------
  172. void CFuncElevator::Precache( void )
  173. {
  174. if ( m_soundStart != NULL_STRING )
  175. {
  176. PrecacheScriptSound( STRING( m_soundStart ) );
  177. }
  178. if ( m_soundStop != NULL_STRING )
  179. {
  180. PrecacheScriptSound( STRING( m_soundStop ) );
  181. }
  182. if ( m_soundDisable != NULL_STRING )
  183. {
  184. PrecacheScriptSound( STRING( m_soundDisable ) );
  185. }
  186. m_currentSound = NULL_STRING;
  187. }
  188. //--------------------------------------------------------------------------------------------------------
  189. int CFuncElevator::GetFloorForHeight( float height ) const
  190. {
  191. int bestFloor = -1;
  192. float bestHeightDelta = 100.0f;
  193. for ( int i=0; i<GetNumFloors(); ++i )
  194. {
  195. const FloorInfo *floor = GetFloor( i );
  196. float heightDelta = fabs( floor->height - height );
  197. if ( heightDelta < bestHeightDelta )
  198. {
  199. bestFloor = i;
  200. bestHeightDelta = heightDelta;
  201. }
  202. }
  203. return bestFloor;
  204. }
  205. //--------------------------------------------------------------------------------------------------------
  206. EHANDLE CFuncElevator::GetButtonForHeight( float height ) const
  207. {
  208. int targetFloorIndex = GetFloorForHeight( height );
  209. if ( targetFloorIndex < 0 || targetFloorIndex >= GetNumFloors() )
  210. return NULL;
  211. const FloorInfo *targetFloor = GetFloor( targetFloorIndex );
  212. if ( !targetFloor )
  213. return NULL;
  214. return targetFloor->button;
  215. }
  216. //--------------------------------------------------------------------------------------------------------
  217. EHANDLE CFuncElevator::GetButtonAtCurrentHeight( void ) const
  218. {
  219. int currentFloorIndex = GetCurrentFloor();
  220. if ( currentFloorIndex < 0 || currentFloorIndex >= GetNumFloors() )
  221. return NULL;
  222. const FloorInfo *currentFloor = GetFloor( currentFloorIndex );
  223. CBaseEntity *bestButton = NULL;
  224. float bestHeightDelta = 100.0f;
  225. for ( int i=0; i<GetNumFloors(); ++i )
  226. {
  227. const FloorInfo *floor = GetFloor( i );
  228. if ( floor == currentFloor )
  229. continue;
  230. CBaseEntity *button = floor->button;
  231. if ( !button )
  232. continue;
  233. float heightDelta = fabs( button->WorldSpaceCenter().z - currentFloor->height );
  234. if ( heightDelta < bestHeightDelta )
  235. {
  236. bestHeightDelta = heightDelta;
  237. bestButton = button;
  238. }
  239. }
  240. return bestButton;
  241. }
  242. //--------------------------------------------------------------------------------------------------------
  243. void CFuncElevator::MoveTo( float destinationZ )
  244. {
  245. if ( !m_enabled )
  246. return;
  247. m_isMoving = true;
  248. m_accelerationTimer.Start();
  249. m_movementStartTime = gpGlobals->curtime;
  250. m_movementStartSpeed = m_currentSpeed;
  251. m_movementStartZ = GetAbsOrigin().z;
  252. m_destinationFloorPosition = destinationZ;
  253. if ( m_soundStart != NULL_STRING )
  254. {
  255. if (m_currentSound == m_soundStart)
  256. {
  257. StopSound(entindex(), CHAN_BODY, (char*)STRING(m_soundStop));
  258. }
  259. else
  260. {
  261. m_currentSound = m_soundStart;
  262. CPASAttenuationFilter filter( this );
  263. EmitSound_t ep;
  264. ep.m_nChannel = CHAN_BODY;
  265. ep.m_pSoundName = (char*)STRING(m_soundStart);
  266. ep.m_flVolume = 1;
  267. ep.m_SoundLevel = SNDLVL_NORM;
  268. EmitSound( filter, entindex(), ep );
  269. }
  270. }
  271. // Find any physics objects on the elevator and destroy them.
  272. // This is to address a problem where players and bots fall through the elevator
  273. // if they stand on a physics object while the elevator is in motion.
  274. Vector lo, hi;
  275. GetCollideable()->WorldSpaceSurroundingBounds( &lo, &hi );
  276. hi.z += HumanHeight;
  277. lo.z -= HumanHeight;
  278. CBaseEntity *entitiesList[ 128 ];
  279. CFlaggedEntitiesEnum enumerator( entitiesList, 128, 0 );
  280. UTIL_EntitiesInBox( lo, hi, &enumerator );
  281. for ( int i=0; i<enumerator.GetCount(); ++i )
  282. {
  283. CBaseEntity *pEnt = entitiesList[i];
  284. // We're only concerned about physics props.
  285. if ( FClassnameIs( pEnt, "prop_physics" ) )
  286. {
  287. // Since it may not be a breakable prop, we can't do TakeDamage.
  288. // Just remove it.
  289. UTIL_Remove( pEnt );
  290. }
  291. }
  292. // Clear think (that stops sounds)
  293. SetThink(NULL);
  294. }
  295. //--------------------------------------------------------------------------------------------------------
  296. void CFuncElevator::InputDisable( inputdata_t &inputdata )
  297. {
  298. if ( !m_enabled )
  299. return;
  300. if ( m_currentSpeed != 0.0f )
  301. {
  302. // Move to our current location, to cancel any current movement...
  303. MoveTo( GetAbsOrigin().z );
  304. }
  305. SetThink(&CFuncElevator::StopMoveSoundThink);
  306. SetNextThink( gpGlobals->curtime + 0.1f );
  307. // ... and disable ourselves to prevent any future movement.
  308. m_enabled = false;
  309. }
  310. //--------------------------------------------------------------------------------------------------------
  311. void CFuncElevator::InputMoveToFloor( inputdata_t &inputdata )
  312. {
  313. if ( !m_enabled )
  314. return;
  315. const char *floorName = inputdata.value.String();
  316. m_targetFloor = NULL;
  317. if ( FStrEq( floorName, "top" ) )
  318. {
  319. MoveTo( m_topFloorPosition.z );
  320. }
  321. else if ( FStrEq( floorName, "bottom" ) )
  322. {
  323. MoveTo( m_bottomFloorPosition.z );
  324. }
  325. else
  326. {
  327. CBaseEntity *target = gEntList.FindEntityByName( NULL, floorName );
  328. if ( target )
  329. {
  330. m_targetFloor = target;
  331. MoveTo( target->GetAbsOrigin().z );
  332. }
  333. else
  334. {
  335. Warning( "Elevator tried to move to bad floor '%s'\n", floorName );
  336. return;
  337. }
  338. }
  339. }
  340. //--------------------------------------------------------------------------------------------------------
  341. /**
  342. * Returns the current floor, or -1 if the elevator is in-between floors
  343. */
  344. int CFuncElevator::GetCurrentFloor( void ) const
  345. {
  346. float currentHeight = GetAbsOrigin().z;
  347. const float Tolerance = 0.5f;
  348. for ( int i=0; i<m_floors.Count(); ++i )
  349. {
  350. const FloorInfo *floor = GetFloor( i );
  351. if ( fabs( currentHeight - floor->height ) < Tolerance )
  352. {
  353. return i;
  354. }
  355. }
  356. return -1; // in-between floors
  357. }
  358. //--------------------------------------------------------------------------------------------------------
  359. /**
  360. * Returns the floor to which the elevator is moving, or the current floor number if the elevator is stopped
  361. */
  362. int CFuncElevator::GetDestinationFloor( void ) const
  363. {
  364. if ( m_currentSpeed != 0.0f )
  365. {
  366. float targetHeight = m_destinationFloorPosition;
  367. const float Tolerance = 0.5f;
  368. for ( int i=0; i<m_floors.Count(); ++i )
  369. {
  370. const FloorInfo *floor = GetFloor( i );
  371. if ( fabs( targetHeight - floor->height ) < Tolerance )
  372. {
  373. return i;
  374. }
  375. }
  376. }
  377. return GetCurrentFloor();
  378. }
  379. //--------------------------------------------------------------------------------------------------------
  380. void CFuncElevator::MoveDone( void )
  381. {
  382. SetContextThink( NULL, TICK_NEVER_THINK, "AccelerationContext" );
  383. m_currentSpeed = 0.0f;
  384. m_isMoving = false;
  385. // Stop sounds at the next think, rather than here as another
  386. // SetPosition call might immediately follow the end of this move
  387. SetThink(&CFuncElevator::StopMoveSoundThink);
  388. SetNextThink( gpGlobals->curtime + 0.1f );
  389. BaseClass::MoveDone();
  390. // Sets a floor string and fires the output
  391. float currentPosition = GetAbsOrigin().z;
  392. if ( currentPosition >= m_topFloorPosition.z )
  393. {
  394. m_OnReachedTop.FireOutput( this, this );
  395. }
  396. else if ( currentPosition <= m_bottomFloorPosition.z )
  397. {
  398. m_OnReachedBottom.FireOutput( this, this );
  399. }
  400. else if ( m_targetFloor.Get() != NULL )
  401. {
  402. CInfoElevatorFloor *floor = dynamic_cast< CInfoElevatorFloor * >(m_targetFloor.Get());
  403. if ( floor )
  404. {
  405. floor->OnReachedFloor( this );
  406. }
  407. }
  408. }
  409. //--------------------------------------------------------------------------------------------------------
  410. void CFuncElevator::Blocked( CBaseEntity *pOther )
  411. {
  412. // Hurt the blocker
  413. if ( m_flBlockDamage )
  414. {
  415. /*
  416. if ( pOther->GetTeamNumber() == TEAM_SURVIVOR )
  417. {
  418. // realistically, we still want to kill them if they're blocked against something not in our move hierarchy.
  419. const trace_t &touchTrace = GetTouchTrace();
  420. if ( touchTrace.DidHitNonWorldEntity() )
  421. {
  422. return;
  423. }
  424. }
  425. */
  426. pOther->TakeDamage( CTakeDamageInfo( this, this, m_flBlockDamage, DMG_CRUSH ) );
  427. }
  428. }
  429. //--------------------------------------------------------------------------------------------------------
  430. void CFuncElevator::StopMoveSoundThink( void )
  431. {
  432. string_t targetSound = ( m_enabled ) ? m_soundStop : m_soundDisable;
  433. if ( m_currentSound != NULL_STRING && ( m_currentSound != targetSound ) )
  434. {
  435. StopSound( entindex(), CHAN_BODY, STRING( m_currentSound ) );
  436. }
  437. if ( targetSound != NULL_STRING && ( m_currentSound != targetSound ) )
  438. {
  439. m_currentSound = targetSound;
  440. CPASAttenuationFilter filter( this );
  441. EmitSound_t ep;
  442. ep.m_nChannel = CHAN_BODY;
  443. ep.m_pSoundName = STRING( targetSound );
  444. ep.m_flVolume = 1;
  445. ep.m_SoundLevel = SNDLVL_NORM;
  446. EmitSound( filter, entindex(), ep );
  447. }
  448. SetThink(NULL);
  449. }
  450. //--------------------------------------------------------------------------------------------------------
  451. bool CFuncElevator::IsPlayerOnElevator( CBasePlayer *player )
  452. {
  453. if ( !player->IsAlive() )
  454. return false;
  455. CBaseEntity *ground = player->GetGroundEntity();
  456. if ( ground )
  457. {
  458. CBaseEntity *groundParent = ground->GetParent();
  459. while ( groundParent && ground->GetParent() && ground->GetParent() != groundParent )
  460. {
  461. groundParent = ground->GetParent();
  462. }
  463. CBaseEntity *groundMoveParent = ground->GetMoveParent();
  464. while ( groundMoveParent && ground->GetMoveParent() && ground->GetMoveParent() != groundMoveParent )
  465. {
  466. groundMoveParent = ground->GetMoveParent();
  467. }
  468. if ( ground == this || groundMoveParent == this || groundParent == this )
  469. {
  470. return true;
  471. }
  472. }
  473. if ( ZombieAirborneElevator.GetBool() )
  474. {
  475. Extent extent;
  476. GetCollideable()->WorldSpaceSurroundingBounds( &extent.lo, &extent.hi );
  477. extent.hi.z += HumanHeight;
  478. if ( extent.Contains( player->GetAbsOrigin() ) )
  479. {
  480. return true;
  481. }
  482. }
  483. return false;
  484. }
  485. //--------------------------------------------------------------------------------------------------------
  486. class ElevatorPlayerCollector
  487. {
  488. public:
  489. ElevatorPlayerCollector( CBaseEntity *elevator, int team )
  490. {
  491. m_elevator = elevator;
  492. m_team = team;
  493. elevator->GetCollideable()->WorldSpaceSurroundingBounds( &m_extent.lo, &m_extent.hi );
  494. m_extent.hi.z += HumanHeight;
  495. }
  496. bool operator()( CBasePlayer *player )
  497. {
  498. if ( !player->IsAlive() )
  499. return true;
  500. if ( player->GetTeamNumber() != m_team && m_team != TEAM_UNASSIGNED )
  501. return true;
  502. CBaseEntity *ground = player->GetGroundEntity();
  503. if ( ground )
  504. {
  505. CBaseEntity *groundParent = ground->GetParent();
  506. while ( groundParent && ground->GetParent() && ground->GetParent() != groundParent )
  507. {
  508. groundParent = ground->GetParent();
  509. }
  510. CBaseEntity *groundMoveParent = ground->GetMoveParent();
  511. while ( groundMoveParent && ground->GetMoveParent() && ground->GetMoveParent() != groundMoveParent )
  512. {
  513. groundMoveParent = ground->GetMoveParent();
  514. }
  515. if ( ground == m_elevator || groundMoveParent == m_elevator || groundParent == m_elevator )
  516. {
  517. m_players.AddToTail( player );
  518. return true;
  519. }
  520. }
  521. if ( m_extent.Contains( player->GetAbsOrigin() ) )
  522. {
  523. m_players.AddToTail( player );
  524. return true;
  525. }
  526. return true;
  527. }
  528. CUtlVector< CBasePlayer * > m_players;
  529. CBaseEntity *m_elevator;
  530. int m_team;
  531. Extent m_extent;
  532. };
  533. //--------------------------------------------------------------------------------------------------------
  534. void CFuncElevator::FindPlayersOnElevator( CUtlVector< CBasePlayer * > *players, int teamNumber )
  535. {
  536. ElevatorPlayerCollector playerCollector( this, teamNumber );
  537. ForEachPlayer( playerCollector );
  538. *players = playerCollector.m_players;
  539. }
  540. //--------------------------------------------------------------------------------------------------------
  541. /**
  542. * Draw any debug text overlays, and return the text offset from the top
  543. */
  544. int CFuncElevator::DrawDebugTextOverlays( void )
  545. {
  546. int text_offset = BaseClass::DrawDebugTextOverlays();
  547. if ( m_debugOverlays & OVERLAY_TEXT_BIT )
  548. {
  549. char tempstr[512];
  550. if ( GetCurrentSpeed() != 0.0f )
  551. {
  552. int destinationFloor = GetDestinationFloor();
  553. const char *floorName = "unknown";
  554. const FloorInfo *floor = GetFloor( destinationFloor );
  555. if ( floor )
  556. {
  557. floorName = STRING( floor->name );
  558. }
  559. Q_snprintf( tempstr, sizeof(tempstr), "Moving at speed %f to floor %d(%s)",
  560. GetCurrentSpeed(), destinationFloor, floorName );
  561. EntityText( text_offset, tempstr, 0 );
  562. ++text_offset;
  563. }
  564. else
  565. {
  566. int currentFloor = GetCurrentFloor();
  567. const char *floorName = "unknown";
  568. const FloorInfo *floor = GetFloor( currentFloor );
  569. if ( floor )
  570. {
  571. floorName = STRING( floor->name );
  572. }
  573. Q_snprintf( tempstr, sizeof(tempstr), "Currently at floor %d(%s)",
  574. currentFloor, floorName );
  575. EntityText( text_offset, tempstr, 0 );
  576. ++text_offset;
  577. }
  578. CBaseEntity *nearbyButton = GetButtonAtCurrentHeight();
  579. if ( nearbyButton )
  580. {
  581. Q_snprintf( tempstr, sizeof(tempstr), "Nearby button is %s",
  582. STRING( nearbyButton->GetEntityName() ) );
  583. EntityText( text_offset++, tempstr, 0 );
  584. }
  585. else
  586. {
  587. EntityText( text_offset++, "No nearby buttons", 0 );
  588. }
  589. for ( int i=0; i<m_floors.Count(); ++i )
  590. {
  591. const FloorInfo &floor = m_floors[i];
  592. const char *floorName = STRING(floor.name);
  593. float floorHeight = floor.height;
  594. const char *buttonName = "<none>";
  595. CBaseEntity *buttonEntity = GetButtonForHeight( floorHeight );
  596. if ( buttonEntity != NULL )
  597. {
  598. buttonName = STRING(buttonEntity->GetEntityName());
  599. }
  600. Q_snprintf( tempstr, sizeof(tempstr), "Floor %s is at %f, triggered by %s",
  601. floorName, floorHeight, buttonName );
  602. EntityText( text_offset++, tempstr, 0 );
  603. }
  604. CUtlVector< CBasePlayer * > players;
  605. FindPlayersOnElevator( &players );
  606. for ( int i=0; i<players.Count(); ++i )
  607. {
  608. Q_snprintf( tempstr, sizeof(tempstr), "Occupant %d: %s", i, players[i]->GetPlayerName() );
  609. EntityText( text_offset, tempstr, 0 );
  610. ++text_offset;
  611. }
  612. }
  613. return text_offset;
  614. }
  615. //--------------------------------------------------------------------------------------------------------