csrd_soldier_statue.sp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /**
  2. * [CSRD] Soldier Statue Spawner
  3. *
  4. * Provides some tooling to spawn Rick May tribute statues on maps that don't support it.
  5. * Only available when the holiday is active.
  6. */
  7. #pragma semicolon 1
  8. #include <sourcemod>
  9. #include <tf2_stocks>
  10. #pragma newdecls required
  11. #include <sdktools>
  12. #include <stocksoup/maps>
  13. #include <stocksoup/log_server>
  14. #define PLUGIN_VERSION "1.0.0"
  15. public Plugin myinfo = {
  16. name = "[CSRD] Soldier Statue Spawner",
  17. author = "nosoop",
  18. description = "Spawns the Rick May tribute statue in custom locations.",
  19. version = PLUGIN_VERSION,
  20. url = "https://git.csrd.science/"
  21. }
  22. // CREATE TABLE IF NOT EXIST locations (map TEXT NOT NULL UNIQUE, xpos REAL, ypos REAL, zpos REAL, pitchang REAL, yawang REAL, rollang REAL);
  23. Database g_SpawnLocations;
  24. public void OnPluginStart() {
  25. RegAdminCmd("csrd_statue_place_aim", CommandSetPositionAim, ADMFLAG_ROOT);
  26. RegAdminCmd("csrd_statue_reload", ReloadStatuePosition, ADMFLAG_ROOT);
  27. char error[128];
  28. g_SpawnLocations = SQLite_UseDatabase("soldier_statues", error, sizeof(error));
  29. if (!g_SpawnLocations) {
  30. SetFailState("Failed to open soldier_statues database: %s", error);
  31. }
  32. }
  33. public void OnMapStart() {
  34. if (!TF2_IsHolidayActive(0x0C)) {
  35. return;
  36. }
  37. char mapName[PLATFORM_MAX_PATH];
  38. GetCurrentMapDisplayName(mapName, sizeof(mapName));
  39. char query[256];
  40. g_SpawnLocations.Format(query, sizeof(query),
  41. "SELECT xpos, ypos, zpos, pitchang, yawang, rollang "
  42. ... "FROM locations WHERE map == '%s';", mapName);
  43. g_SpawnLocations.Query(OnSpawnLocationLoaded, query);
  44. }
  45. public Action CommandSetPositionAim(int client, int argc) {
  46. float position[3], origin[3];
  47. GetClientAbsOrigin(client, origin);
  48. GetClientAimEndPosition(client, position);
  49. float direction[3], angles[3];
  50. MakeVectorFromPoints(position, origin, direction);
  51. GetVectorAngles(direction, angles);
  52. SetStatuePosition(position, angles);
  53. return Plugin_Handled;
  54. }
  55. public Action ReloadStatuePosition(int client, int argc) {
  56. char mapName[PLATFORM_MAX_PATH];
  57. GetCurrentMapDisplayName(mapName, sizeof(mapName));
  58. char query[256];
  59. g_SpawnLocations.Format(query, sizeof(query),
  60. "SELECT xpos, ypos, zpos, pitchang, yawang, rollang "
  61. ... "FROM locations WHERE map == '%s';", mapName);
  62. g_SpawnLocations.Query(OnSpawnLocationLoaded, query);
  63. return Plugin_Handled;
  64. }
  65. void SetStatuePosition(const float position[3], const float angles[3]) {
  66. TeleportEntity(GetSoldierStatueEntity(), position, angles, NULL_VECTOR);
  67. char mapName[PLATFORM_MAX_PATH];
  68. GetCurrentMapDisplayName(mapName, sizeof(mapName));
  69. char query[256];
  70. g_SpawnLocations.Format(query, sizeof(query),
  71. "REPLACE INTO locations(map, xpos, ypos, zpos, pitchang, yawang, rollang)"
  72. ... "VALUES ('%s', %f, %f, %f, %f, %f, %f);",
  73. mapName, position[0], position[1], position[2], angles[0], angles[1], angles[2]);
  74. g_SpawnLocations.Query(OnSpawnLocationSaved, query);
  75. }
  76. int GetSoldierStatueEntity() {
  77. int statue = FindEntityByClassname(-1, "entity_soldier_statue");
  78. if (!IsValidEntity(statue)) {
  79. statue = CreateEntityByName("entity_soldier_statue");
  80. DispatchSpawn(statue);
  81. }
  82. return statue;
  83. }
  84. public void OnSpawnLocationLoaded(Database db, DBResultSet results, const char[] error,
  85. any data) {
  86. if (!results.FetchRow()) {
  87. if (FindEntityByClassname(-1, "entity_soldier_statue") == -1) {
  88. char mapName[PLATFORM_MAX_PATH];
  89. GetCurrentMapDisplayName(mapName, sizeof(mapName));
  90. LogServer("%s does not have a Soldier statue", mapName);
  91. }
  92. return;
  93. }
  94. float position[3], angles[3];
  95. position[0] = results.FetchFloat(0);
  96. position[1] = results.FetchFloat(1);
  97. position[2] = results.FetchFloat(2);
  98. angles[0] = results.FetchFloat(3);
  99. angles[1] = results.FetchFloat(4);
  100. angles[2] = results.FetchFloat(5);
  101. TeleportEntity(GetSoldierStatueEntity(), position, angles, NULL_VECTOR);
  102. LogServer("Spawned Soldier statue");
  103. }
  104. public void OnSpawnLocationSaved(Database db, DBResultSet results, const char[] error,
  105. any data) {
  106. return;
  107. }
  108. /**
  109. * Obtains the end position in the world that a client is looking at.
  110. *
  111. * @param client The client to check.
  112. * @param vecAimPoint The buffer to store the position in the world on success.
  113. * @param filter The trace entity filter function to call when performing the check.
  114. * @param data A value to pass into the filter function callback.
  115. *
  116. * @return true if a valid position is found
  117. */
  118. stock bool GetClientAimEndPosition(int client, float vecAimPoint[3],
  119. TraceEntityFilter filter = INVALID_FUNCTION, any data = 0) {
  120. float angEye[3], vecStartPosition[3];
  121. GetClientEyePosition(client, vecStartPosition);
  122. GetClientEyeAngles(client, angEye);
  123. Handle trace = TR_TraceRayFilterEx(vecStartPosition, angEye, MASK_SHOT, RayType_Infinite,
  124. filter != INVALID_FUNCTION? filter : TraceEntityFilterPlayer, data);
  125. bool result = TR_DidHit(trace);
  126. if (TR_DidHit(trace)) {
  127. TR_GetEndPosition(vecAimPoint, trace);
  128. }
  129. delete trace;
  130. return result;
  131. }
  132. /**
  133. * Returns a trace ray handle in the client's aim direction.
  134. */
  135. stock Handle GetClientAimEndTrace(int client, int flags,
  136. TraceEntityFilter filter = INVALID_FUNCTION, any data = 0) {
  137. float angEye[3], vecStartPosition[3];
  138. GetClientEyePosition(client, vecStartPosition);
  139. GetClientEyeAngles(client, angEye);
  140. return TR_TraceRayFilterEx(vecStartPosition, angEye, flags, RayType_Infinite,
  141. filter != INVALID_FUNCTION? filter : TraceEntityFilterPlayer, data);
  142. }
  143. /**
  144. * Internal callback function that ignores player entities.
  145. */
  146. static stock bool TraceEntityFilterPlayer(int entity, int contentsMask) {
  147. return entity > MaxClients;
  148. }