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.

362 lines
10 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "ai_default.h"
  9. #include "scriptedtarget.h"
  10. #include "entitylist.h"
  11. #include "ndebugoverlay.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. //=========================================================
  15. // Interactions
  16. //=========================================================
  17. int g_interactionScriptedTarget = 0;
  18. LINK_ENTITY_TO_CLASS( scripted_target, CScriptedTarget );
  19. BEGIN_DATADESC( CScriptedTarget )
  20. DEFINE_FIELD( m_vLastPosition, FIELD_POSITION_VECTOR ),
  21. DEFINE_KEYFIELD( m_iDisabled, FIELD_INTEGER, "StartDisabled" ),
  22. DEFINE_KEYFIELD( m_iszEntity, FIELD_STRING, "m_iszEntity" ),
  23. DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "m_flRadius" ),
  24. DEFINE_KEYFIELD( m_nMoveSpeed, FIELD_INTEGER, "MoveSpeed" ),
  25. DEFINE_KEYFIELD( m_flPauseDuration, FIELD_FLOAT, "PauseDuration" ),
  26. DEFINE_FIELD( m_flPauseDoneTime, FIELD_TIME ),
  27. DEFINE_KEYFIELD( m_flEffectDuration, FIELD_FLOAT, "EffectDuration" ),
  28. // Function Pointers
  29. DEFINE_THINKFUNC( ScriptThink ),
  30. // Inputs
  31. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  32. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  33. // Outputs
  34. DEFINE_OUTPUT(m_AtTarget, "AtTarget" ),
  35. DEFINE_OUTPUT(m_LeaveTarget, "LeaveTarget" ),
  36. END_DATADESC()
  37. //------------------------------------------------------------------------------
  38. // Purpose:
  39. //------------------------------------------------------------------------------
  40. void CScriptedTarget::InputEnable( inputdata_t &inputdata )
  41. {
  42. TurnOn();
  43. }
  44. //------------------------------------------------------------------------------
  45. // Purpose:
  46. //------------------------------------------------------------------------------
  47. void CScriptedTarget::InputDisable( inputdata_t &inputdata )
  48. {
  49. TurnOff();
  50. }
  51. //-----------------------------------------------------------------------------
  52. // Purpose:
  53. //-----------------------------------------------------------------------------
  54. void CScriptedTarget::TurnOn( void )
  55. {
  56. m_vLastPosition = GetAbsOrigin();
  57. SetThink( &CScriptedTarget::ScriptThink );
  58. m_iDisabled = false;
  59. SetNextThink( gpGlobals->curtime );
  60. }
  61. //------------------------------------------------------------------------------
  62. // Purpose:
  63. //------------------------------------------------------------------------------
  64. void CScriptedTarget::TurnOff( void )
  65. {
  66. SetThink( NULL );
  67. m_iDisabled = true;
  68. // If I have a target entity, free him
  69. if (GetTarget())
  70. {
  71. CAI_BaseNPC* pNPC = GetTarget()->MyNPCPointer();
  72. pNPC->DispatchInteraction( g_interactionScriptedTarget, NULL, NULL );
  73. }
  74. }
  75. //------------------------------------------------------------------------------
  76. // Purpose:
  77. //------------------------------------------------------------------------------
  78. void CScriptedTarget::Spawn( void )
  79. {
  80. if (g_interactionScriptedTarget == 0)
  81. {
  82. g_interactionScriptedTarget = CBaseCombatCharacter::GetInteractionID();
  83. }
  84. SetSolid( SOLID_NONE );
  85. m_vLastPosition = GetAbsOrigin();
  86. if (!m_iDisabled )
  87. {
  88. TurnOn();
  89. }
  90. }
  91. //------------------------------------------------------------------------------
  92. // Purpose:
  93. //------------------------------------------------------------------------------
  94. CScriptedTarget* CScriptedTarget::NextScriptedTarget(void)
  95. {
  96. // ----------------------------------------------------------------------
  97. // If I just hit my target, set how long I'm supposed to pause here
  98. // ----------------------------------------------------------------------
  99. if (m_flPauseDoneTime == 0)
  100. {
  101. m_flPauseDoneTime = gpGlobals->curtime + m_flPauseDuration;
  102. m_AtTarget.FireOutput( GetTarget(), this );
  103. }
  104. // -------------------------------------------------------------
  105. // If I'm done pausing move on to next burn target
  106. // -------------------------------------------------------------
  107. if (gpGlobals->curtime >= m_flPauseDoneTime)
  108. {
  109. m_flPauseDoneTime = 0;
  110. // ----------------------------------------------------------
  111. // Fire output that current Scripted target has been reached
  112. // ----------------------------------------------------------
  113. m_LeaveTarget.FireOutput( GetTarget(), this );
  114. // ------------------------------------------------------------
  115. // Get next target.
  116. // ------------------------------------------------------------
  117. CScriptedTarget* pNextTarget = ((CScriptedTarget*)GetNextTarget());
  118. // --------------------------------------------
  119. // Fire output if last one has been reached
  120. // --------------------------------------------
  121. if (!pNextTarget)
  122. {
  123. TurnOff();
  124. SetTarget( NULL );
  125. }
  126. // ------------------------------------------------
  127. // Otherwise, turn myself off, the next target on
  128. // and pass on my target entity
  129. // ------------------------------------------------
  130. else
  131. {
  132. // ----------------------------------------------------
  133. // Make sure there is a LOS between these two targets
  134. // ----------------------------------------------------
  135. trace_t tr;
  136. UTIL_TraceLine(GetAbsOrigin(), pNextTarget->GetAbsOrigin(), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr);
  137. if (tr.fraction != 1.0)
  138. {
  139. Warning( "WARNING: Scripted Target from (%s) to (%s) is occluded!\n",GetDebugName(),pNextTarget->GetDebugName() );
  140. }
  141. pNextTarget->TurnOn();
  142. pNextTarget->SetTarget( GetTarget() );
  143. SetTarget( NULL );
  144. TurnOff();
  145. }
  146. // --------------------------------------------
  147. // Return new target
  148. // --------------------------------------------
  149. return pNextTarget;
  150. }
  151. // -------------------------------------------------------------
  152. // Otherwise keep the same scripted target until pause is done
  153. // -------------------------------------------------------------
  154. else
  155. {
  156. return this;
  157. }
  158. }
  159. //------------------------------------------------------------------------------
  160. // Purpose:
  161. //------------------------------------------------------------------------------
  162. CBaseEntity* CScriptedTarget::FindEntity( void )
  163. {
  164. // ---------------------------------------------------
  165. // First try to find the entity by name
  166. // ---------------------------------------------------
  167. CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, m_iszEntity );
  168. if (pEntity && pEntity->GetFlags() & FL_NPC)
  169. {
  170. CAI_BaseNPC* pNPC = pEntity->MyNPCPointer();
  171. if (pNPC->DispatchInteraction( g_interactionScriptedTarget, NULL, this ))
  172. {
  173. return pEntity;
  174. }
  175. }
  176. // ---------------------------------------------------
  177. // If that fails, assume we were given a classname
  178. // and find nearest entity in radius of that class
  179. // ---------------------------------------------------
  180. float flNearestDist = MAX_COORD_RANGE;
  181. CBaseEntity* pNearestEnt = NULL;
  182. CBaseEntity* pTestEnt = NULL;
  183. for ( CEntitySphereQuery sphere( GetAbsOrigin(), m_flRadius ); ( pTestEnt = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
  184. {
  185. if (pTestEnt->GetFlags() & FL_NPC)
  186. {
  187. if (FClassnameIs( pTestEnt, STRING(m_iszEntity)))
  188. {
  189. float flTestDist = (pTestEnt->GetAbsOrigin() - GetAbsOrigin()).Length();
  190. if (flTestDist < flNearestDist)
  191. {
  192. flNearestDist = flTestDist;
  193. pNearestEnt = pTestEnt;
  194. }
  195. }
  196. }
  197. }
  198. // UNDONE: If nearest fails, try next nearest
  199. if (pNearestEnt)
  200. {
  201. CAI_BaseNPC* pNPC = pNearestEnt->MyNPCPointer();
  202. if (pNPC->DispatchInteraction( g_interactionScriptedTarget, NULL, this ))
  203. {
  204. return pNearestEnt;
  205. }
  206. }
  207. return NULL;
  208. }
  209. //------------------------------------------------------------------------------
  210. // Purpose:
  211. //------------------------------------------------------------------------------
  212. void CScriptedTarget::ScriptThink( void )
  213. {
  214. // --------------------------------------------
  215. // If I don't have target entity look for one
  216. // --------------------------------------------
  217. if (GetTarget() == NULL)
  218. {
  219. m_flPauseDoneTime = 0;
  220. SetTarget( FindEntity() );
  221. }
  222. SetNextThink( gpGlobals->curtime + 0.1f );
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Purpose: Draw any debug text overlays
  226. // Output : Current text offset from the top
  227. //-----------------------------------------------------------------------------
  228. int CScriptedTarget::DrawDebugTextOverlays(void)
  229. {
  230. // Skip AIClass debug overlays
  231. int text_offset = CBaseEntity::DrawDebugTextOverlays();
  232. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  233. {
  234. // --------------
  235. // Print State
  236. // --------------
  237. char tempstr[512];
  238. if (m_iDisabled)
  239. {
  240. Q_strncpy(tempstr,"State: Off",sizeof(tempstr));
  241. }
  242. else
  243. {
  244. Q_strncpy(tempstr,"State: On",sizeof(tempstr));
  245. }
  246. EntityText(text_offset,tempstr,0);
  247. text_offset++;
  248. // -----------------
  249. // Print Next Entity
  250. // -----------------
  251. CBaseEntity *pTarget = GetNextTarget();
  252. if (pTarget)
  253. {
  254. Q_snprintf(tempstr,sizeof(tempstr),"Next: %s",pTarget->GetDebugName() );
  255. }
  256. else
  257. {
  258. Q_strncpy(tempstr,"Next: -NONE-",sizeof(tempstr));
  259. }
  260. EntityText(text_offset,tempstr,0);
  261. text_offset++;
  262. // --------------
  263. // Print Target
  264. // --------------
  265. if (GetTarget()!=NULL)
  266. {
  267. Q_snprintf(tempstr,sizeof(tempstr),"User: %s",GetTarget()->GetDebugName() );
  268. }
  269. else if (m_iDisabled)
  270. {
  271. Q_strncpy(tempstr,"User: -NONE-",sizeof(tempstr));
  272. }
  273. else
  274. {
  275. Q_strncpy(tempstr,"User: -LOOKING-",sizeof(tempstr));
  276. }
  277. EntityText(text_offset,tempstr,0);
  278. text_offset++;
  279. }
  280. return text_offset;
  281. }
  282. //-----------------------------------------------------------------------------
  283. // Purpose: Override base class to add display of paths
  284. //-----------------------------------------------------------------------------
  285. void CScriptedTarget::DrawDebugGeometryOverlays(void)
  286. {
  287. // ----------------------------------------------
  288. // Draw line to next target is bbox is selected
  289. // ----------------------------------------------
  290. if (m_debugOverlays & (OVERLAY_BBOX_BIT|OVERLAY_ABSBOX_BIT))
  291. {
  292. if (m_iDisabled)
  293. {
  294. NDebugOverlay::Box(GetAbsOrigin(), Vector(-5,-5,-5), Vector(5,5,5), 200,100,100, 0 ,0);
  295. }
  296. else
  297. {
  298. NDebugOverlay::Cross3D(m_vLastPosition, Vector(-8,-8,-8),Vector(8,8,8),255,0,0,true,0.1);
  299. NDebugOverlay::Box(GetAbsOrigin(), Vector(-5,-5,-5), Vector(5,5,5), 255,0,0, 0 ,0);
  300. NDebugOverlay::Line(GetAbsOrigin(),m_vLastPosition,255,0,0,true,0.0);
  301. }
  302. CBaseEntity *pTarget = GetNextTarget();
  303. if (pTarget)
  304. {
  305. NDebugOverlay::Line(GetAbsOrigin(),pTarget->GetAbsOrigin(),200,100,100,true,0.0);
  306. }
  307. if (GetTarget() != NULL)
  308. {
  309. NDebugOverlay::Line(GetAbsOrigin(),GetTarget()->EyePosition(),0,255,0,true,0.0);
  310. }
  311. }
  312. CBaseEntity::DrawDebugGeometryOverlays();
  313. }