g_utils.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. // g_utils.c -- misc utility functions for game module
  4. #include "g_local.h"
  5. /*
  6. =============
  7. G_Find
  8. Searches all active entities for the next one that validates the given callback.
  9. Searches beginning at the edict after from, or the beginning if nullptr
  10. nullptr will be returned if the end of the list is reached.
  11. =============
  12. */
  13. edict_t *G_Find(edict_t *from, std::function<bool(edict_t *e)> matcher)
  14. {
  15. if (!from)
  16. from = g_edicts;
  17. else
  18. from++;
  19. for (; from < &g_edicts[globals.num_edicts]; from++)
  20. {
  21. if (!from->inuse)
  22. continue;
  23. if (matcher(from))
  24. return from;
  25. }
  26. return nullptr;
  27. }
  28. /*
  29. =================
  30. findradius
  31. Returns entities that have origins within a spherical area
  32. findradius (origin, radius)
  33. =================
  34. */
  35. edict_t *findradius(edict_t *from, const vec3_t &org, float rad)
  36. {
  37. vec3_t eorg;
  38. int j;
  39. if (!from)
  40. from = g_edicts;
  41. else
  42. from++;
  43. for (; from < &g_edicts[globals.num_edicts]; from++)
  44. {
  45. if (!from->inuse)
  46. continue;
  47. if (from->solid == SOLID_NOT)
  48. continue;
  49. for (j = 0; j < 3; j++)
  50. eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j]) * 0.5f);
  51. if (eorg.length() > rad)
  52. continue;
  53. return from;
  54. }
  55. return nullptr;
  56. }
  57. /*
  58. =============
  59. G_PickTarget
  60. Searches all active entities for the next one that holds
  61. the matching string at fieldofs in the structure.
  62. Searches beginning at the edict after from, or the beginning if nullptr
  63. nullptr will be returned if the end of the list is reached.
  64. =============
  65. */
  66. constexpr size_t MAXCHOICES = 8;
  67. edict_t *G_PickTarget(const char *targetname)
  68. {
  69. edict_t *ent = nullptr;
  70. int num_choices = 0;
  71. edict_t *choice[MAXCHOICES];
  72. if (!targetname)
  73. {
  74. gi.Com_Print("G_PickTarget called with nullptr targetname\n");
  75. return nullptr;
  76. }
  77. while (1)
  78. {
  79. ent = G_FindByString<&edict_t::targetname>(ent, targetname);
  80. if (!ent)
  81. break;
  82. choice[num_choices++] = ent;
  83. if (num_choices == MAXCHOICES)
  84. break;
  85. }
  86. if (!num_choices)
  87. {
  88. gi.Com_PrintFmt("G_PickTarget: target {} not found\n", targetname);
  89. return nullptr;
  90. }
  91. return choice[irandom(num_choices)];
  92. }
  93. THINK(Think_Delay) (edict_t *ent) -> void
  94. {
  95. G_UseTargets(ent, ent->activator);
  96. G_FreeEdict(ent);
  97. }
  98. void G_PrintActivationMessage(edict_t *ent, edict_t *activator, bool coop_global)
  99. {
  100. //
  101. // print the message
  102. //
  103. if ((ent->message) && !(activator->svflags & SVF_MONSTER))
  104. {
  105. if (coop_global && coop->integer)
  106. gi.LocBroadcast_Print(PRINT_CENTER, "{}", ent->message);
  107. else
  108. gi.LocCenter_Print(activator, "{}", ent->message);
  109. // [Paril-KEX] allow non-noisy centerprints
  110. if (ent->noise_index >= 0)
  111. {
  112. if (ent->noise_index)
  113. gi.sound(activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0);
  114. else
  115. gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/talk1.wav"), 1, ATTN_NORM, 0);
  116. }
  117. }
  118. }
  119. void G_MonsterKilled(edict_t *self);
  120. /*
  121. ==============================
  122. G_UseTargets
  123. the global "activator" should be set to the entity that initiated the firing.
  124. If self.delay is set, a DelayedUse entity will be created that will actually
  125. do the SUB_UseTargets after that many seconds have passed.
  126. Centerprints any self.message to the activator.
  127. Search for (string)targetname in all entities that
  128. match (string)self.target and call their .use function
  129. ==============================
  130. */
  131. void G_UseTargets(edict_t *ent, edict_t *activator)
  132. {
  133. edict_t *t;
  134. //
  135. // check for a delay
  136. //
  137. if (ent->delay)
  138. {
  139. // create a temp object to fire at a later time
  140. t = G_Spawn();
  141. t->classname = "DelayedUse";
  142. t->nextthink = level.time + gtime_t::from_sec(ent->delay);
  143. t->think = Think_Delay;
  144. t->activator = activator;
  145. if (!activator)
  146. gi.Com_Print("Think_Delay with no activator\n");
  147. t->message = ent->message;
  148. t->target = ent->target;
  149. t->killtarget = ent->killtarget;
  150. return;
  151. }
  152. //
  153. // print the message
  154. //
  155. G_PrintActivationMessage(ent, activator, true);
  156. //
  157. // kill killtargets
  158. //
  159. if (ent->killtarget)
  160. {
  161. t = nullptr;
  162. while ((t = G_FindByString<&edict_t::targetname>(t, ent->killtarget)))
  163. {
  164. if (t->teammaster)
  165. {
  166. // PMM - if this entity is part of a chain, cleanly remove it
  167. if (t->flags & FL_TEAMSLAVE)
  168. {
  169. for (edict_t *master = t->teammaster; master; master = master->teamchain)
  170. {
  171. if (master->teamchain == t)
  172. {
  173. master->teamchain = t->teamchain;
  174. break;
  175. }
  176. }
  177. }
  178. // [Paril-KEX] remove teammaster too
  179. else if (t->flags & FL_TEAMMASTER)
  180. {
  181. t->teammaster->flags &= ~FL_TEAMMASTER;
  182. edict_t *new_master = t->teammaster->teamchain;
  183. if (new_master)
  184. {
  185. new_master->flags |= FL_TEAMMASTER;
  186. new_master->flags &= ~FL_TEAMSLAVE;
  187. for (edict_t *m = new_master; m; m = m->teamchain)
  188. m->teammaster = new_master;
  189. }
  190. }
  191. }
  192. // [Paril-KEX] if we killtarget a monster, clean up properly
  193. if (t->svflags & SVF_MONSTER)
  194. {
  195. if (!t->deadflag && !(t->monsterinfo.aiflags & AI_DO_NOT_COUNT) && !(t->spawnflags & SPAWNFLAG_MONSTER_DEAD))
  196. G_MonsterKilled(t);
  197. }
  198. // PMM
  199. G_FreeEdict(t);
  200. if (!ent->inuse)
  201. {
  202. gi.Com_Print("entity was removed while using killtargets\n");
  203. return;
  204. }
  205. }
  206. }
  207. //
  208. // fire targets
  209. //
  210. if (ent->target)
  211. {
  212. t = nullptr;
  213. while ((t = G_FindByString<&edict_t::targetname>(t, ent->target)))
  214. {
  215. // doors fire area portals in a specific way
  216. if (!Q_strcasecmp(t->classname, "func_areaportal") &&
  217. (!Q_strcasecmp(ent->classname, "func_door") || !Q_strcasecmp(ent->classname, "func_door_rotating")
  218. || !Q_strcasecmp(ent->classname, "func_door_secret") || !Q_strcasecmp(ent->classname, "func_water")))
  219. continue;
  220. if (t == ent)
  221. {
  222. gi.Com_Print("WARNING: Entity used itself.\n");
  223. }
  224. else
  225. {
  226. if (t->use)
  227. t->use(t, ent, activator);
  228. }
  229. if (!ent->inuse)
  230. {
  231. gi.Com_Print("entity was removed while using targets\n");
  232. return;
  233. }
  234. }
  235. }
  236. }
  237. constexpr vec3_t VEC_UP = { 0, -1, 0 };
  238. constexpr vec3_t MOVEDIR_UP = { 0, 0, 1 };
  239. constexpr vec3_t VEC_DOWN = { 0, -2, 0 };
  240. constexpr vec3_t MOVEDIR_DOWN = { 0, 0, -1 };
  241. void G_SetMovedir(vec3_t &angles, vec3_t &movedir)
  242. {
  243. if (angles == VEC_UP)
  244. {
  245. movedir = MOVEDIR_UP;
  246. }
  247. else if (angles == VEC_DOWN)
  248. {
  249. movedir = MOVEDIR_DOWN;
  250. }
  251. else
  252. {
  253. AngleVectors(angles, movedir, nullptr, nullptr);
  254. }
  255. angles = {};
  256. }
  257. char *G_CopyString(const char *in, int32_t tag)
  258. {
  259. if(!in)
  260. return nullptr;
  261. const size_t amt = strlen(in) + 1;
  262. char *const out = static_cast<char *>(gi.TagMalloc(amt, tag));
  263. Q_strlcpy(out, in, amt);
  264. return out;
  265. }
  266. void G_InitEdict(edict_t *e)
  267. {
  268. // ROGUE
  269. // FIXME -
  270. // this fixes a bug somewhere that is setting "nextthink" for an entity that has
  271. // already been released. nextthink is being set to FRAME_TIME_S after level.time,
  272. // since freetime = nextthink - FRAME_TIME_S
  273. if (e->nextthink)
  274. e->nextthink = 0_ms;
  275. // ROGUE
  276. e->inuse = true;
  277. e->sv.init = false;
  278. e->classname = "noclass";
  279. e->gravity = 1.0;
  280. e->s.number = e - g_edicts;
  281. // PGM - do this before calling the spawn function so it can be overridden.
  282. e->gravityVector[0] = 0.0;
  283. e->gravityVector[1] = 0.0;
  284. e->gravityVector[2] = -1.0;
  285. // PGM
  286. }
  287. /*
  288. =================
  289. G_Spawn
  290. Either finds a free edict, or allocates a new one.
  291. Try to avoid reusing an entity that was recently freed, because it
  292. can cause the client to think the entity morphed into something else
  293. instead of being removed and recreated, which can cause interpolated
  294. angles and bad trails.
  295. =================
  296. */
  297. edict_t *G_Spawn()
  298. {
  299. uint32_t i;
  300. edict_t *e;
  301. e = &g_edicts[game.maxclients + 1];
  302. for (i = game.maxclients + 1; i < globals.num_edicts; i++, e++)
  303. {
  304. // the first couple seconds of server time can involve a lot of
  305. // freeing and allocating, so relax the replacement policy
  306. if (!e->inuse && (e->freetime < 2_sec || level.time - e->freetime > 500_ms))
  307. {
  308. G_InitEdict(e);
  309. return e;
  310. }
  311. }
  312. if (i == game.maxentities)
  313. gi.Com_Error("ED_Alloc: no free edicts");
  314. globals.num_edicts++;
  315. G_InitEdict(e);
  316. return e;
  317. }
  318. /*
  319. =================
  320. G_FreeEdict
  321. Marks the edict as free
  322. =================
  323. */
  324. THINK(G_FreeEdict) (edict_t *ed) -> void
  325. {
  326. // already freed
  327. if (!ed->inuse)
  328. return;
  329. gi.unlinkentity(ed); // unlink from world
  330. if ((ed - g_edicts) <= (ptrdiff_t) (game.maxclients + BODY_QUEUE_SIZE))
  331. {
  332. #ifdef _DEBUG
  333. gi.Com_Print("tried to free special edict\n");
  334. #endif
  335. return;
  336. }
  337. gi.Bot_UnRegisterEdict( ed );
  338. int32_t id = ed->spawn_count + 1;
  339. memset(ed, 0, sizeof(*ed));
  340. ed->s.number = ed - g_edicts;
  341. ed->classname = "freed";
  342. ed->freetime = level.time;
  343. ed->inuse = false;
  344. ed->spawn_count = id;
  345. ed->sv.init = false;
  346. }
  347. BoxEdictsResult_t G_TouchTriggers_BoxFilter(edict_t *hit, void *)
  348. {
  349. if (!hit->touch)
  350. return BoxEdictsResult_t::Skip;
  351. return BoxEdictsResult_t::Keep;
  352. }
  353. /*
  354. ============
  355. G_TouchTriggers
  356. ============
  357. */
  358. void G_TouchTriggers(edict_t *ent)
  359. {
  360. int i, num;
  361. static edict_t *touch[MAX_EDICTS];
  362. edict_t *hit;
  363. // dead things don't activate triggers!
  364. if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
  365. return;
  366. num = gi.BoxEdicts(ent->absmin, ent->absmax, touch, MAX_EDICTS, AREA_TRIGGERS, G_TouchTriggers_BoxFilter, nullptr);
  367. // be careful, it is possible to have an entity in this
  368. // list removed before we get to it (killtriggered)
  369. for (i = 0; i < num; i++)
  370. {
  371. hit = touch[i];
  372. if (!hit->inuse)
  373. continue;
  374. if (!hit->touch)
  375. continue;
  376. hit->touch(hit, ent, null_trace, true);
  377. }
  378. }
  379. // [Paril-KEX] scan for projectiles between our movement positions
  380. // to see if we need to collide against them
  381. void G_TouchProjectiles(edict_t *ent, vec3_t previous_origin)
  382. {
  383. struct skipped_projectile
  384. {
  385. edict_t *projectile;
  386. int32_t spawn_count;
  387. };
  388. // a bit ugly, but we'll store projectiles we are ignoring here.
  389. static std::vector<skipped_projectile> skipped;
  390. while (true)
  391. {
  392. trace_t tr = gi.trace(previous_origin, ent->mins, ent->maxs, ent->s.origin, ent, ent->clipmask | CONTENTS_PROJECTILE);
  393. if (tr.fraction == 1.0f)
  394. break;
  395. else if (!(tr.ent->svflags & SVF_PROJECTILE))
  396. break;
  397. // always skip this projectile since certain conditions may cause the projectile
  398. // to not disappear immediately
  399. tr.ent->svflags &= ~SVF_PROJECTILE;
  400. skipped.push_back({ tr.ent, tr.ent->spawn_count });
  401. // if we're both players and it's coop, allow the projectile to "pass" through
  402. if (ent->client && tr.ent->owner && tr.ent->owner->client && !G_ShouldPlayersCollide(true))
  403. continue;
  404. G_Impact(ent, tr);
  405. }
  406. for (auto &skip : skipped)
  407. if (skip.projectile->inuse && skip.projectile->spawn_count == skip.spawn_count)
  408. skip.projectile->svflags |= SVF_PROJECTILE;
  409. skipped.clear();
  410. }
  411. /*
  412. ==============================================================================
  413. Kill box
  414. ==============================================================================
  415. */
  416. /*
  417. =================
  418. KillBox
  419. Kills all entities that would touch the proposed new positioning
  420. of ent.
  421. =================
  422. */
  423. BoxEdictsResult_t KillBox_BoxFilter(edict_t *hit, void *)
  424. {
  425. if (!hit->solid || !hit->takedamage || hit->solid == SOLID_TRIGGER)
  426. return BoxEdictsResult_t::Skip;
  427. return BoxEdictsResult_t::Keep;
  428. }
  429. bool KillBox(edict_t *ent, bool from_spawning, mod_id_t mod, bool bsp_clipping)
  430. {
  431. // don't telefrag as spectator...
  432. if (ent->movetype == MOVETYPE_NOCLIP)
  433. return true;
  434. contents_t mask = CONTENTS_MONSTER | CONTENTS_PLAYER;
  435. // [Paril-KEX] don't gib other players in coop if we're not colliding
  436. if (from_spawning && ent->client && coop->integer && !G_ShouldPlayersCollide(false))
  437. mask &= ~CONTENTS_PLAYER;
  438. int i, num;
  439. static edict_t *touch[MAX_EDICTS];
  440. edict_t *hit;
  441. num = gi.BoxEdicts(ent->absmin, ent->absmax, touch, MAX_EDICTS, AREA_SOLID, KillBox_BoxFilter, nullptr);
  442. for (i = 0; i < num; i++)
  443. {
  444. hit = touch[i];
  445. if (hit == ent)
  446. continue;
  447. else if (!hit->inuse || !hit->takedamage || !hit->solid || hit->solid == SOLID_TRIGGER || hit->solid == SOLID_BSP)
  448. continue;
  449. else if (hit->client && !(mask & CONTENTS_PLAYER))
  450. continue;
  451. if ((ent->solid == SOLID_BSP || (ent->svflags & SVF_HULL)) && bsp_clipping)
  452. {
  453. trace_t clip = gi.clip(ent, hit->s.origin, hit->mins, hit->maxs, hit->s.origin, G_GetClipMask(hit));
  454. if (clip.fraction == 1.0f)
  455. continue;
  456. }
  457. // [Paril-KEX] don't allow telefragging of friends in coop.
  458. // the player that is about to be telefragged will have collision
  459. // disabled until another time.
  460. if (ent->client && hit->client && coop->integer)
  461. {
  462. hit->clipmask &= ~CONTENTS_PLAYER;
  463. ent->clipmask &= ~CONTENTS_PLAYER;
  464. continue;
  465. }
  466. T_Damage(hit, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, mod);
  467. }
  468. return true; // all clear
  469. }