priority.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** Confidential - Westwood Studios ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Commando *
  23. * *
  24. * $Archive:: /Commando/Code/Commando/priority.cpp $*
  25. * *
  26. * $Author:: Steve_t $*
  27. * *
  28. * $Modtime:: 2/23/02 12:35p $*
  29. * *
  30. * $Revision:: 15 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "priority.h"
  36. #include <math.h>
  37. #include "wwdebug.h"
  38. #include "wwmath.h"
  39. #include "vector3.h"
  40. #include "gameobjmanager.h"
  41. #include "soldier.h"
  42. #include "humanphys.h"
  43. #include "apppackettypes.h"
  44. #include "vehicle.h"
  45. #include "useroptions.h"
  46. #include "playermanager.h"
  47. #include "wwprofile.h"
  48. //
  49. // Class statics
  50. //
  51. float cPriority::MaxDistance = 300.0F;
  52. const float cPriority::TURRET_FACTOR = 0.2f;
  53. const float cPriority::VEHICLE_FACTOR = 0.85f;
  54. const float cPriority::SOLDIER_FACTOR = 1.0f;
  55. const float cPriority::SOLDIER_IN_VEHICLE_FACTOR = 0.1f;
  56. const float cPriority::BUILDING_FACTOR = 0.2f;
  57. //-----------------------------------------------------------------------------
  58. //
  59. // Compute the priority of the given netobject to the given client
  60. //
  61. float
  62. cPriority::Compute_Object_Priority
  63. (
  64. int client_id,
  65. const Vector3 & client_pos,
  66. NetworkObjectClass * p_netobject,
  67. bool do_it_anyway,
  68. SoldierGameObj * client_soldier
  69. )
  70. {
  71. WWPROFILE("ObjPri");
  72. WWASSERT(client_id > 0);
  73. WWASSERT(p_netobject != NULL);
  74. //
  75. // Compute the priority of this object to the given client at his given position.
  76. // Priority depends on physical distance. Objects with no physical location will
  77. // have a priority of 1.
  78. //
  79. float priority = 0.0f;
  80. if (p_netobject->Is_Client_Dirty(client_id) || do_it_anyway)
  81. {
  82. float distance = Get_Object_Distance(client_pos, p_netobject);
  83. //
  84. // Priority simply decreases linearly with distance and is zero at MaxDistance.
  85. //
  86. priority = 1 - distance / MaxDistance;
  87. if (priority > 0.0f) {
  88. priority *= Compute_Facing_Factor(client_id, client_pos, p_netobject, client_soldier);
  89. }
  90. if (priority > 0.0f) {
  91. priority *= Compute_Type_Factor(p_netobject);
  92. }
  93. if (priority > 0.0f) {
  94. priority *= Compute_Relevance_Factor(client_id, p_netobject, client_soldier);
  95. }
  96. // Add a bit of a curve to have low priority objects drop away faster.
  97. if (priority > 0.0f && priority < 1.0f) {
  98. //float bendy = WWMath::Fast_Sin(priority * DEG_TO_RAD(90.0f));
  99. //priority = (priority + bendy) / 2.0f;
  100. float bendy = WWMath::Fast_Asin(priority);
  101. bendy = (RAD_TO_DEG(bendy)) / 90.0f;
  102. priority = (priority + bendy) / 2.0f;
  103. }
  104. }
  105. //
  106. // Override all priority calculations if the client hints that he badly needs an
  107. // update for this object. The client will hint if there is a vehicle or soldier nearby
  108. // that hasn't been updated for a suspiciously long time.
  109. //
  110. if (p_netobject->Get_Client_Hint_Count(client_id) > 0)
  111. {
  112. priority = 1.0f;
  113. p_netobject->Reset_Client_Hint_Count(client_id);
  114. /*
  115. WWDEBUG_SAY(("cPriority::Compute_Object_Priority %5.3f on object %d due to client %d hint.\n",
  116. priority, p_netobject->Get_Network_ID(), client_id));
  117. */
  118. }
  119. priority = WWMath::Clamp(priority, 0.0f, 1.0f);
  120. return priority;
  121. }
  122. //-----------------------------------------------------------------------------
  123. float
  124. cPriority::Compute_Facing_Factor
  125. (
  126. int client_id,
  127. const Vector3 & client_pos,
  128. NetworkObjectClass * p_netobject,
  129. SoldierGameObj *client_soldier
  130. )
  131. {
  132. WWASSERT(client_id > 0);
  133. WWASSERT(p_netobject != NULL);
  134. float facing_factor = 1;
  135. SoldierGameObj * p_soldier = client_soldier;
  136. if (p_soldier == NULL) {
  137. p_soldier = GameObjManager::Find_Soldier_Of_Client_ID(client_id);
  138. }
  139. if (p_soldier != NULL)
  140. {
  141. WWASSERT(p_soldier->Peek_Human_Phys() != NULL);
  142. float client_facing = p_soldier->Peek_Human_Phys()->Get_Heading();
  143. Vector3 subject_position;
  144. if (p_netobject->Get_World_Position(subject_position))
  145. {
  146. Vector3 position_delta = subject_position - client_pos;
  147. float subject_facing = WWMath::Atan2(position_delta.Y, position_delta.X);
  148. float facing_dif = ::fabs(subject_facing - client_facing);
  149. if (facing_dif > WWMATH_PI)
  150. {
  151. facing_dif = 2 * WWMATH_PI - facing_dif;
  152. }
  153. facing_factor -= facing_dif / WWMATH_PI * cUserOptions::MaxFacingPenalty.Get();
  154. }
  155. }
  156. return facing_factor;
  157. }
  158. //-----------------------------------------------------------------------------
  159. float
  160. cPriority::Get_Object_Distance
  161. (
  162. const Vector3 & client_pos,
  163. NetworkObjectClass * p_netobject
  164. )
  165. {
  166. WWASSERT(p_netobject != NULL);
  167. //
  168. // Objects without a physical location will return a distance of zero.
  169. //
  170. float distance = 0;
  171. //
  172. // Get the object's world position (if it has one)
  173. //
  174. Vector3 position;
  175. if (p_netobject->Get_World_Position(position))
  176. {
  177. //
  178. // Simple distance calculation based on the distance
  179. // between points.
  180. //
  181. distance = (position - client_pos).Length();
  182. }
  183. return distance;
  184. }
  185. //-----------------------------------------------------------------------------
  186. float
  187. cPriority::Compute_Type_Factor
  188. (
  189. NetworkObjectClass *p_netobject
  190. )
  191. {
  192. float type_factor = 1.0f;
  193. char type = p_netobject->Get_App_Packet_Type();
  194. switch (type) {
  195. case APPPACKETTYPE_SOLDIER:
  196. type_factor = SOLDIER_FACTOR;
  197. break;
  198. case APPPACKETTYPE_TURRET:
  199. type_factor = TURRET_FACTOR;
  200. break;
  201. case APPPACKETTYPE_VEHICLE:
  202. type_factor = VEHICLE_FACTOR;
  203. break;
  204. case APPPACKETTYPE_BUILDING:
  205. type_factor = BUILDING_FACTOR;
  206. break;
  207. default:
  208. type_factor = 1.0f;
  209. }
  210. return(type_factor);
  211. }
  212. //-----------------------------------------------------------------------------
  213. float
  214. cPriority::Compute_Relevance_Factor
  215. (
  216. int client_id,
  217. NetworkObjectClass * p_netobject,
  218. SoldierGameObj *client_soldier
  219. )
  220. {
  221. WWASSERT(p_netobject != NULL);
  222. //
  223. // This bumps the priority of objects that you are shooting or that are
  224. // shooting at you.
  225. //
  226. bool is_relevant = false;
  227. int id = p_netobject->Get_Network_ID();
  228. SoldierGameObj * p_my_soldier = client_soldier;
  229. if (p_my_soldier == NULL) {
  230. p_my_soldier = GameObjManager::Find_Soldier_Of_Client_ID(client_id);
  231. }
  232. VehicleGameObj * p_my_vehicle = NULL;
  233. if (p_my_soldier != NULL) {
  234. p_my_vehicle = GameObjManager::Find_Vehicle_Occupied_By(p_my_soldier);
  235. }
  236. if (p_my_soldier != NULL &&
  237. (p_my_soldier->Get_Last_Object_Id_I_Damaged() == id ||
  238. p_my_soldier->Get_Last_Object_Id_I_Got_Damaged_By() == id)) {
  239. is_relevant = true;
  240. }
  241. if (p_my_vehicle != NULL &&
  242. (p_my_vehicle->Get_Last_Object_Id_I_Damaged() == id ||
  243. p_my_vehicle->Get_Last_Object_Id_I_Got_Damaged_By() == id)) {
  244. is_relevant = true;
  245. }
  246. float factor = 1.0f;
  247. if (!is_relevant) {
  248. factor -= cUserOptions::IrrelevancePenalty.Get();
  249. }
  250. WWASSERT(factor > 0);
  251. return factor;
  252. }
  253. float
  254. cPriority::Compute_Object_Priority_2
  255. (
  256. int client_id,
  257. const Vector3 & client_pos,
  258. NetworkObjectClass * p_netobject,
  259. bool do_it_anyway,
  260. SoldierGameObj * client_soldier
  261. )
  262. {
  263. WWPROFILE("ObjPri");
  264. WWASSERT(client_id > 0);
  265. WWASSERT(p_netobject != NULL);
  266. //
  267. // Compute the priority of this object to the given client at his given position.
  268. // Priority depends on physical distance. Objects with no physical location will
  269. // have a priority of 1.
  270. //
  271. float priority = 0.0f;
  272. if (p_netobject->Is_Client_Dirty(client_id) || do_it_anyway)
  273. {
  274. float distance = Get_Object_Distance_2(client_pos, p_netobject);
  275. //
  276. // Priority simply decreases linearly with distance and is zero at MaxDistance.
  277. //
  278. priority = 1 - distance / MaxDistance;
  279. if (priority > 0.0f) {
  280. priority *= Compute_Facing_Factor_2(client_id, client_pos, p_netobject, client_soldier);
  281. }
  282. if (priority > 0.0f) {
  283. priority *= Compute_Type_Factor_2(p_netobject, distance);
  284. }
  285. if (priority > 0.0f) {
  286. priority *= Compute_Relevance_Factor_2(client_id, p_netobject, client_soldier);
  287. }
  288. // Add a bit of a curve to have low priority objects drop away faster.
  289. if (priority > 0.0f && priority < 1.0f) {
  290. //float bendy = WWMath::Fast_Sin(priority * DEG_TO_RAD(90.0f));
  291. //priority = (priority + bendy) / 2.0f;
  292. float bendy = WWMath::Fast_Asin(priority);
  293. bendy = (RAD_TO_DEG(bendy)) / 90.0f;
  294. priority = (priority + bendy) / 2.0f;
  295. }
  296. }
  297. priority = WWMath::Clamp(priority, 0.0f, 1.0f);
  298. return priority;
  299. }
  300. //-----------------------------------------------------------------------------
  301. float
  302. cPriority::Compute_Facing_Factor_2
  303. (
  304. int client_id,
  305. const Vector3 & client_pos,
  306. NetworkObjectClass * p_netobject,
  307. SoldierGameObj *client_soldier
  308. )
  309. {
  310. WWASSERT(client_id > 0);
  311. WWASSERT(p_netobject != NULL);
  312. float facing_factor = 1;
  313. SoldierGameObj * p_soldier = client_soldier;
  314. if (p_soldier == NULL) {
  315. p_soldier = GameObjManager::Find_Soldier_Of_Client_ID(client_id);
  316. }
  317. if (p_soldier != NULL)
  318. {
  319. HumanPhysClass *hphys = p_soldier->Peek_Human_Phys();
  320. WWASSERT(hphys != NULL);
  321. float client_facing = hphys->Get_Heading();
  322. Vector3 subject_position;
  323. if (p_netobject->Get_World_Position(subject_position))
  324. {
  325. Vector3 position_delta = subject_position - client_pos;
  326. float subject_facing = WWMath::Atan2(position_delta.Y, position_delta.X);
  327. float facing_dif = ::fabs(subject_facing - client_facing);
  328. if (facing_dif > WWMATH_PI)
  329. {
  330. facing_dif = 2 * WWMATH_PI - facing_dif;
  331. }
  332. facing_factor -= facing_dif / WWMATH_PI * cUserOptions::MaxFacingPenalty.Get();
  333. }
  334. }
  335. return facing_factor;
  336. }
  337. //-----------------------------------------------------------------------------
  338. float
  339. cPriority::Get_Object_Distance_2
  340. (
  341. const Vector3 & client_pos,
  342. NetworkObjectClass * p_netobject
  343. )
  344. {
  345. WWASSERT(p_netobject != NULL);
  346. //
  347. // Objects without a physical location will return a distance of zero.
  348. //
  349. float distance = 0;
  350. //
  351. // Get the object's world position (if it has one)
  352. //
  353. Vector3 position;
  354. if (p_netobject->Get_World_Position(position))
  355. {
  356. //
  357. // Simple distance calculation based on the distance
  358. // between points.
  359. //
  360. distance = (position - client_pos).Length();
  361. }
  362. return distance;
  363. }
  364. //-----------------------------------------------------------------------------
  365. float
  366. cPriority::Compute_Type_Factor_2
  367. (
  368. NetworkObjectClass *p_netobject,
  369. float distance
  370. )
  371. {
  372. float type_factor = 1.0f;
  373. char type = p_netobject->Get_App_Packet_Type();
  374. switch (type) {
  375. case APPPACKETTYPE_SOLDIER:
  376. {
  377. type_factor = SOLDIER_FACTOR;
  378. SoldierGameObj *soldier = (SoldierGameObj*) p_netobject;
  379. if (soldier->Is_In_Vehicle()) {
  380. type_factor = SOLDIER_IN_VEHICLE_FACTOR;
  381. }
  382. }
  383. break;
  384. case APPPACKETTYPE_TURRET:
  385. {
  386. type_factor = TURRET_FACTOR;
  387. float range_filter = p_netobject->Get_Filter_Distance();
  388. if (distance > range_filter) {
  389. type_factor = 0.0f;
  390. } else {
  391. if (distance > 100) {
  392. type_factor = type_factor / 2.0f;
  393. }
  394. }
  395. break;
  396. }
  397. case APPPACKETTYPE_VEHICLE:
  398. type_factor = VEHICLE_FACTOR;
  399. break;
  400. case APPPACKETTYPE_BUILDING:
  401. type_factor = BUILDING_FACTOR;
  402. break;
  403. default:
  404. type_factor = 1.0f;
  405. }
  406. return(type_factor);
  407. }
  408. //-----------------------------------------------------------------------------
  409. float
  410. cPriority::Compute_Relevance_Factor_2
  411. (
  412. int client_id,
  413. NetworkObjectClass * p_netobject,
  414. SoldierGameObj *client_soldier
  415. )
  416. {
  417. WWASSERT(p_netobject != NULL);
  418. //
  419. // This bumps the priority of objects that you are shooting or that are
  420. // shooting at you.
  421. //
  422. bool is_relevant = false;
  423. int id = p_netobject->Get_Network_ID();
  424. SoldierGameObj * p_my_soldier = client_soldier;
  425. VehicleGameObj * p_my_vehicle = NULL;
  426. if (p_my_soldier == NULL) {
  427. p_my_soldier = GameObjManager::Find_Soldier_Of_Client_ID(client_id);
  428. }
  429. if (p_my_soldier != NULL) {
  430. if (p_my_soldier->Is_In_Vehicle()) {
  431. p_my_vehicle = GameObjManager::Find_Vehicle_Occupied_By(p_my_soldier);
  432. }
  433. if ((p_my_soldier->Get_Last_Object_Id_I_Damaged() == id ||
  434. p_my_soldier->Get_Last_Object_Id_I_Got_Damaged_By() == id)) {
  435. is_relevant = true;
  436. }
  437. }
  438. if (p_my_vehicle != NULL &&
  439. (p_my_vehicle->Get_Last_Object_Id_I_Damaged() == id ||
  440. p_my_vehicle->Get_Last_Object_Id_I_Got_Damaged_By() == id)) {
  441. is_relevant = true;
  442. }
  443. float factor = 1.0f;
  444. if (!is_relevant) {
  445. factor -= cUserOptions::IrrelevancePenalty.Get();
  446. }
  447. WWASSERT(factor > 0);
  448. return factor;
  449. }