g_cmds.cpp 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. #include "g_local.h"
  4. #include "m_player.h"
  5. void SelectNextItem(edict_t *ent, item_flags_t itflags, bool menu = true)
  6. {
  7. gclient_t *cl;
  8. item_id_t i, index;
  9. gitem_t *it;
  10. cl = ent->client;
  11. // ZOID
  12. if (menu && cl->menu)
  13. {
  14. PMenu_Next(ent);
  15. return;
  16. }
  17. else if (menu && cl->chase_target)
  18. {
  19. ChaseNext(ent);
  20. return;
  21. }
  22. // ZOID
  23. // scan for the next valid one
  24. for (i = static_cast<item_id_t>(IT_NULL + 1); i <= IT_TOTAL; i = static_cast<item_id_t>(i + 1))
  25. {
  26. index = static_cast<item_id_t>((cl->pers.selected_item + i) % IT_TOTAL);
  27. if (!cl->pers.inventory[index])
  28. continue;
  29. it = &itemlist[index];
  30. if (!it->use)
  31. continue;
  32. if (!(it->flags & itflags))
  33. continue;
  34. cl->pers.selected_item = index;
  35. cl->pers.selected_item_time = level.time + SELECTED_ITEM_TIME;
  36. cl->ps.stats[STAT_SELECTED_ITEM_NAME] = CS_ITEMS + index;
  37. return;
  38. }
  39. cl->pers.selected_item = IT_NULL;
  40. }
  41. void SelectPrevItem(edict_t *ent, item_flags_t itflags)
  42. {
  43. gclient_t *cl;
  44. item_id_t i, index;
  45. gitem_t *it;
  46. cl = ent->client;
  47. // ZOID
  48. if (cl->menu)
  49. {
  50. PMenu_Prev(ent);
  51. return;
  52. }
  53. else if (cl->chase_target)
  54. {
  55. ChasePrev(ent);
  56. return;
  57. }
  58. // ZOID
  59. // scan for the next valid one
  60. for (i = static_cast<item_id_t>(IT_NULL + 1); i <= IT_TOTAL; i = static_cast<item_id_t>(i + 1))
  61. {
  62. index = static_cast<item_id_t>((cl->pers.selected_item + IT_TOTAL - i) % IT_TOTAL);
  63. if (!cl->pers.inventory[index])
  64. continue;
  65. it = &itemlist[index];
  66. if (!it->use)
  67. continue;
  68. if (!(it->flags & itflags))
  69. continue;
  70. cl->pers.selected_item = index;
  71. cl->pers.selected_item_time = level.time + SELECTED_ITEM_TIME;
  72. cl->ps.stats[STAT_SELECTED_ITEM_NAME] = CS_ITEMS + index;
  73. return;
  74. }
  75. cl->pers.selected_item = IT_NULL;
  76. }
  77. void ValidateSelectedItem(edict_t *ent)
  78. {
  79. gclient_t *cl;
  80. cl = ent->client;
  81. if (cl->pers.inventory[cl->pers.selected_item])
  82. return; // valid
  83. SelectNextItem(ent, IF_ANY, false);
  84. }
  85. //=================================================================================
  86. inline bool G_CheatCheck(edict_t *ent)
  87. {
  88. if (game.maxclients > 1 && !sv_cheats->integer)
  89. {
  90. gi.LocClient_Print(ent, PRINT_HIGH, "$g_need_cheats");
  91. return false;
  92. }
  93. return true;
  94. }
  95. static void SpawnAndGiveItem(edict_t *ent, item_id_t id)
  96. {
  97. gitem_t *it = GetItemByIndex(id);
  98. if (!it)
  99. return;
  100. edict_t *it_ent = G_Spawn();
  101. it_ent->classname = it->classname;
  102. SpawnItem(it_ent, it);
  103. if (it_ent->inuse)
  104. {
  105. Touch_Item(it_ent, ent, null_trace, true);
  106. if (it_ent->inuse)
  107. G_FreeEdict(it_ent);
  108. }
  109. }
  110. /*
  111. ==================
  112. Cmd_Give_f
  113. Give items to a client
  114. ==================
  115. */
  116. void Cmd_Give_f(edict_t *ent)
  117. {
  118. const char *name;
  119. gitem_t *it;
  120. item_id_t index;
  121. int i;
  122. bool give_all;
  123. edict_t *it_ent;
  124. if (!G_CheatCheck(ent))
  125. return;
  126. name = gi.args();
  127. if (Q_strcasecmp(name, "all") == 0)
  128. give_all = true;
  129. else
  130. give_all = false;
  131. if (give_all || Q_strcasecmp(gi.argv(1), "health") == 0)
  132. {
  133. if (gi.argc() == 3)
  134. ent->health = atoi(gi.argv(2));
  135. else
  136. ent->health = ent->max_health;
  137. if (!give_all)
  138. return;
  139. }
  140. if (give_all || Q_strcasecmp(name, "weapons") == 0)
  141. {
  142. for (i = 0; i < IT_TOTAL; i++)
  143. {
  144. it = itemlist + i;
  145. if (!it->pickup)
  146. continue;
  147. if (!(it->flags & IF_WEAPON))
  148. continue;
  149. ent->client->pers.inventory[i] += 1;
  150. }
  151. if (!give_all)
  152. return;
  153. }
  154. if (give_all || Q_strcasecmp(name, "ammo") == 0)
  155. {
  156. if (give_all)
  157. SpawnAndGiveItem(ent, IT_ITEM_PACK);
  158. for (i = 0; i < IT_TOTAL; i++)
  159. {
  160. it = itemlist + i;
  161. if (!it->pickup)
  162. continue;
  163. if (!(it->flags & IF_AMMO))
  164. continue;
  165. Add_Ammo(ent, it, 1000);
  166. }
  167. if (!give_all)
  168. return;
  169. }
  170. if (give_all || Q_strcasecmp(name, "armor") == 0)
  171. {
  172. ent->client->pers.inventory[IT_ARMOR_JACKET] = 0;
  173. ent->client->pers.inventory[IT_ARMOR_COMBAT] = 0;
  174. ent->client->pers.inventory[IT_ARMOR_BODY] = GetItemByIndex(IT_ARMOR_BODY)->armor_info->max_count;
  175. if (!give_all)
  176. return;
  177. }
  178. if (give_all)
  179. {
  180. SpawnAndGiveItem(ent, IT_ITEM_POWER_SHIELD);
  181. if (!give_all)
  182. return;
  183. }
  184. if (give_all)
  185. {
  186. for (i = 0; i < IT_TOTAL; i++)
  187. {
  188. it = itemlist + i;
  189. if (!it->pickup)
  190. continue;
  191. // ROGUE
  192. if (it->flags & (IF_ARMOR | IF_WEAPON | IF_AMMO | IF_NOT_GIVEABLE | IF_TECH))
  193. continue;
  194. else if (it->pickup == CTFPickup_Flag)
  195. continue;
  196. else if ((it->flags & IF_HEALTH) && !it->use)
  197. continue;
  198. // ROGUE
  199. ent->client->pers.inventory[i] = (it->flags & IF_KEY) ? 8 : 1;
  200. }
  201. G_CheckPowerArmor(ent);
  202. ent->client->pers.power_cubes = 0xFF;
  203. return;
  204. }
  205. it = FindItem(name);
  206. if (!it)
  207. {
  208. name = gi.argv(1);
  209. it = FindItem(name);
  210. }
  211. if (!it)
  212. it = FindItemByClassname(name);
  213. if (!it)
  214. {
  215. gi.LocClient_Print(ent, PRINT_HIGH, "$g_unknown_item");
  216. return;
  217. }
  218. // ROGUE
  219. if (it->flags & IF_NOT_GIVEABLE)
  220. {
  221. gi.LocClient_Print(ent, PRINT_HIGH, "$g_not_giveable");
  222. return;
  223. }
  224. // ROGUE
  225. index = it->id;
  226. if (!it->pickup)
  227. {
  228. ent->client->pers.inventory[index] = 1;
  229. return;
  230. }
  231. if (it->flags & IF_AMMO)
  232. {
  233. if (gi.argc() == 3)
  234. ent->client->pers.inventory[index] = atoi(gi.argv(2));
  235. else
  236. ent->client->pers.inventory[index] += it->quantity;
  237. }
  238. else
  239. {
  240. it_ent = G_Spawn();
  241. it_ent->classname = it->classname;
  242. SpawnItem(it_ent, it);
  243. // PMM - since some items don't actually spawn when you say to ..
  244. if (!it_ent->inuse)
  245. return;
  246. // pmm
  247. Touch_Item(it_ent, ent, null_trace, true);
  248. if (it_ent->inuse)
  249. G_FreeEdict(it_ent);
  250. }
  251. }
  252. void Cmd_SetPOI_f(edict_t *self)
  253. {
  254. if (!G_CheatCheck(self))
  255. return;
  256. level.current_poi = self->s.origin;
  257. level.valid_poi = true;
  258. }
  259. void Cmd_CheckPOI_f(edict_t *self)
  260. {
  261. if (!G_CheatCheck(self))
  262. return;
  263. if (!level.valid_poi)
  264. return;
  265. char visible_pvs = gi.inPVS(self->s.origin, level.current_poi, false) ? 'y' : 'n';
  266. char visible_pvs_portals = gi.inPVS(self->s.origin, level.current_poi, true) ? 'y' : 'n';
  267. char visible_phs = gi.inPHS(self->s.origin, level.current_poi, false) ? 'y' : 'n';
  268. char visible_phs_portals = gi.inPHS(self->s.origin, level.current_poi, true) ? 'y' : 'n';
  269. gi.Com_PrintFmt("pvs {} + portals {}, phs {} + portals {}\n", visible_pvs, visible_pvs_portals, visible_phs, visible_phs_portals);
  270. }
  271. // [Paril-KEX]
  272. static void Cmd_Target_f(edict_t *ent)
  273. {
  274. if (!G_CheatCheck(ent))
  275. return;
  276. ent->target = gi.argv(1);
  277. G_UseTargets(ent, ent);
  278. ent->target = nullptr;
  279. }
  280. /*
  281. ==================
  282. Cmd_God_f
  283. Sets client to godmode
  284. argv(0) god
  285. ==================
  286. */
  287. void Cmd_God_f(edict_t *ent)
  288. {
  289. const char *msg;
  290. if (!G_CheatCheck(ent))
  291. return;
  292. ent->flags ^= FL_GODMODE;
  293. if (!(ent->flags & FL_GODMODE))
  294. msg = "godmode OFF\n";
  295. else
  296. msg = "godmode ON\n";
  297. gi.LocClient_Print(ent, PRINT_HIGH, msg);
  298. }
  299. void ED_ParseField(const char *key, const char *value, edict_t *ent);
  300. /*
  301. ==================
  302. Cmd_Immortal_f
  303. Sets client to immortal - take damage but never go below 1 hp
  304. argv(0) immortal
  305. ==================
  306. */
  307. void Cmd_Immortal_f(edict_t *ent)
  308. {
  309. const char *msg;
  310. if (!G_CheatCheck(ent))
  311. return;
  312. ent->flags ^= FL_IMMORTAL;
  313. if (!(ent->flags & FL_IMMORTAL))
  314. msg = "immortal OFF\n";
  315. else
  316. msg = "immortal ON\n";
  317. gi.LocClient_Print(ent, PRINT_HIGH, msg);
  318. }
  319. /*
  320. =================
  321. Cmd_Spawn_f
  322. Spawn class name
  323. argv(0) spawn
  324. argv(1) <classname>
  325. argv(2+n) "key"...
  326. argv(3+n) "value"...
  327. =================
  328. */
  329. void Cmd_Spawn_f(edict_t *ent)
  330. {
  331. if (!G_CheatCheck(ent))
  332. return;
  333. solid_t backup = ent->solid;
  334. ent->solid = SOLID_NOT;
  335. gi.linkentity(ent);
  336. edict_t* other = G_Spawn();
  337. other->classname = gi.argv(1);
  338. other->s.origin = ent->s.origin + (AngleVectors(ent->s.angles).forward * 24.f);
  339. other->s.angles[1] = ent->s.angles[1];
  340. st = {};
  341. if (gi.argc() > 3)
  342. {
  343. for (int i = 2; i < gi.argc(); i += 2)
  344. ED_ParseField(gi.argv(i), gi.argv(i + 1), other);
  345. }
  346. ED_CallSpawn(other);
  347. if (other->inuse)
  348. {
  349. vec3_t forward, end;
  350. AngleVectors(ent->client->v_angle, forward, nullptr, nullptr);
  351. end = ent->s.origin;
  352. end[2] += ent->viewheight;
  353. end += (forward * 8192);
  354. trace_t tr = gi.traceline(ent->s.origin + vec3_t{0.f, 0.f, (float) ent->viewheight}, end, other, MASK_SHOT | CONTENTS_MONSTERCLIP);
  355. other->s.origin = tr.endpos;
  356. for (int32_t i = 0; i < 3; i++)
  357. {
  358. if (tr.plane.normal[i] > 0)
  359. other->s.origin[i] -= other->mins[i] * tr.plane.normal[i];
  360. else
  361. other->s.origin[i] += other->maxs[i] * -tr.plane.normal[i];
  362. }
  363. while (gi.trace(other->s.origin, other->mins, other->maxs, other->s.origin, other,
  364. MASK_SHOT | CONTENTS_MONSTERCLIP)
  365. .startsolid)
  366. {
  367. float dx = other->mins[0] - other->maxs[0];
  368. float dy = other->mins[1] - other->maxs[1];
  369. other->s.origin += forward * -sqrtf(dx * dx + dy * dy);
  370. if ((other->s.origin - ent->s.origin).dot(forward) < 0)
  371. {
  372. gi.Client_Print(ent, PRINT_HIGH, "Couldn't find a suitable spawn location\n");
  373. G_FreeEdict(other);
  374. break;
  375. }
  376. }
  377. if (other->inuse)
  378. gi.linkentity(other);
  379. if ((other->svflags & SVF_MONSTER) && other->think)
  380. other->think(other);
  381. }
  382. ent->solid = backup;
  383. gi.linkentity(ent);
  384. }
  385. /*
  386. =================
  387. Cmd_Spawn_f
  388. Telepo'
  389. argv(0) teleport
  390. argv(1) x
  391. argv(2) y
  392. argv(3) z
  393. =================
  394. */
  395. void Cmd_Teleport_f(edict_t *ent)
  396. {
  397. if (!G_CheatCheck(ent))
  398. return;
  399. if (gi.argc() < 4)
  400. {
  401. gi.LocClient_Print(ent, PRINT_HIGH, "Not enough args; teleport x y z\n");
  402. return;
  403. }
  404. ent->s.origin[0] = (float) atof(gi.argv(1));
  405. ent->s.origin[1] = (float) atof(gi.argv(2));
  406. ent->s.origin[2] = (float) atof(gi.argv(3));
  407. if (gi.argc() >= 4)
  408. {
  409. float pitch = (float) atof(gi.argv(4));
  410. float yaw = (float) atof(gi.argv(5));
  411. float roll = (float) atof(gi.argv(6));
  412. vec3_t ang { pitch, yaw, roll };
  413. ent->client->ps.pmove.delta_angles = ( ang - ent->client->resp.cmd_angles );
  414. ent->client->ps.viewangles = {};
  415. ent->client->v_angle = {};
  416. }
  417. gi.linkentity(ent);
  418. }
  419. /*
  420. ==================
  421. Cmd_Notarget_f
  422. Sets client to notarget
  423. argv(0) notarget
  424. ==================
  425. */
  426. void Cmd_Notarget_f(edict_t *ent)
  427. {
  428. const char *msg;
  429. if (!G_CheatCheck(ent))
  430. return;
  431. ent->flags ^= FL_NOTARGET;
  432. if (!(ent->flags & FL_NOTARGET))
  433. msg = "notarget OFF\n";
  434. else
  435. msg = "notarget ON\n";
  436. gi.LocClient_Print(ent, PRINT_HIGH, msg);
  437. }
  438. /*
  439. ==================
  440. Cmd_Novisible_f
  441. Sets client to "super notarget"
  442. argv(0) notarget
  443. ==================
  444. */
  445. void Cmd_Novisible_f(edict_t *ent)
  446. {
  447. const char *msg;
  448. if (!G_CheatCheck(ent))
  449. return;
  450. ent->flags ^= FL_NOVISIBLE;
  451. if (!(ent->flags & FL_NOVISIBLE))
  452. msg = "novisible OFF\n";
  453. else
  454. msg = "novisible ON\n";
  455. gi.LocClient_Print(ent, PRINT_HIGH, msg);
  456. }
  457. void Cmd_AlertAll_f(edict_t *ent)
  458. {
  459. if (!G_CheatCheck(ent))
  460. return;
  461. for (size_t i = 0; i < globals.num_edicts; i++)
  462. {
  463. edict_t *t = &g_edicts[i];
  464. if (!t->inuse || t->health <= 0 || !(t->svflags & SVF_MONSTER))
  465. continue;
  466. t->enemy = ent;
  467. FoundTarget(t);
  468. }
  469. }
  470. /*
  471. ==================
  472. Cmd_Noclip_f
  473. argv(0) noclip
  474. ==================
  475. */
  476. void Cmd_Noclip_f(edict_t *ent)
  477. {
  478. const char *msg;
  479. if (!G_CheatCheck(ent))
  480. return;
  481. if (ent->movetype == MOVETYPE_NOCLIP)
  482. {
  483. ent->movetype = MOVETYPE_WALK;
  484. msg = "noclip OFF\n";
  485. }
  486. else
  487. {
  488. ent->movetype = MOVETYPE_NOCLIP;
  489. msg = "noclip ON\n";
  490. }
  491. gi.LocClient_Print(ent, PRINT_HIGH, msg);
  492. }
  493. /*
  494. ==================
  495. Cmd_Use_f
  496. Use an inventory item
  497. ==================
  498. */
  499. void Cmd_Use_f(edict_t *ent)
  500. {
  501. item_id_t index;
  502. gitem_t *it;
  503. const char *s;
  504. if (ent->health <= 0 || ent->deadflag)
  505. return;
  506. s = gi.args();
  507. const char *cmd = gi.argv(0);
  508. if (!Q_strcasecmp(cmd, "use_index") || !Q_strcasecmp(cmd, "use_index_only"))
  509. {
  510. it = GetItemByIndex((item_id_t) atoi(s));
  511. }
  512. else
  513. {
  514. it = FindItem(s);
  515. }
  516. if (!it)
  517. {
  518. gi.LocClient_Print(ent, PRINT_HIGH, "$g_unknown_item_name", s);
  519. return;
  520. }
  521. if (!it->use)
  522. {
  523. gi.LocClient_Print(ent, PRINT_HIGH, "$g_item_not_usable");
  524. return;
  525. }
  526. index = it->id;
  527. // Paril: Use_Weapon handles weapon availability
  528. if (!(it->flags & IF_WEAPON) && !ent->client->pers.inventory[index])
  529. {
  530. gi.LocClient_Print(ent, PRINT_HIGH, "$g_out_of_item", it->pickup_name);
  531. return;
  532. }
  533. // allow weapon chains for use
  534. ent->client->no_weapon_chains = !!strcmp(gi.argv(0), "use") && !!strcmp(gi.argv(0), "use_index");
  535. it->use(ent, it);
  536. ValidateSelectedItem(ent);
  537. }
  538. /*
  539. ==================
  540. Cmd_Drop_f
  541. Drop an inventory item
  542. ==================
  543. */
  544. void Cmd_Drop_f(edict_t *ent)
  545. {
  546. item_id_t index;
  547. gitem_t *it;
  548. const char *s;
  549. if (ent->health <= 0 || ent->deadflag)
  550. return;
  551. // ZOID--special case for tech powerups
  552. if (Q_strcasecmp(gi.args(), "tech") == 0)
  553. {
  554. it = CTFWhat_Tech(ent);
  555. if (it)
  556. {
  557. it->drop(ent, it);
  558. ValidateSelectedItem(ent);
  559. }
  560. return;
  561. }
  562. // ZOID
  563. s = gi.args();
  564. const char *cmd = gi.argv(0);
  565. if (!Q_strcasecmp(cmd, "drop_index"))
  566. {
  567. it = GetItemByIndex((item_id_t) atoi(s));
  568. }
  569. else
  570. {
  571. it = FindItem(s);
  572. }
  573. if (!it)
  574. {
  575. gi.LocClient_Print(ent, PRINT_HIGH, "Unknown item : {}\n", s);
  576. return;
  577. }
  578. if (!it->drop)
  579. {
  580. gi.LocClient_Print(ent, PRINT_HIGH, "$g_item_not_droppable");
  581. return;
  582. }
  583. index = it->id;
  584. if (!ent->client->pers.inventory[index])
  585. {
  586. gi.LocClient_Print(ent, PRINT_HIGH, "$g_out_of_item", it->pickup_name);
  587. return;
  588. }
  589. it->drop(ent, it);
  590. ValidateSelectedItem(ent);
  591. }
  592. /*
  593. =================
  594. Cmd_Inven_f
  595. =================
  596. */
  597. void Cmd_Inven_f(edict_t *ent)
  598. {
  599. int i;
  600. gclient_t *cl;
  601. cl = ent->client;
  602. cl->showscores = false;
  603. cl->showhelp = false;
  604. globals.server_flags &= ~SERVER_FLAG_SLOW_TIME;
  605. // ZOID
  606. if (ent->client->menu)
  607. {
  608. PMenu_Close(ent);
  609. ent->client->update_chase = true;
  610. return;
  611. }
  612. // ZOID
  613. if (cl->showinventory)
  614. {
  615. cl->showinventory = false;
  616. return;
  617. }
  618. // ZOID
  619. if (G_TeamplayEnabled() && cl->resp.ctf_team == CTF_NOTEAM)
  620. {
  621. CTFOpenJoinMenu(ent);
  622. return;
  623. }
  624. // ZOID
  625. cl->showinventory = true;
  626. gi.WriteByte(svc_inventory);
  627. for (i = 0; i < IT_TOTAL; i++)
  628. gi.WriteShort(cl->pers.inventory[i]);
  629. for (; i < MAX_ITEMS; i++)
  630. gi.WriteShort(0);
  631. gi.unicast(ent, true);
  632. }
  633. /*
  634. =================
  635. Cmd_InvUse_f
  636. =================
  637. */
  638. void Cmd_InvUse_f(edict_t *ent)
  639. {
  640. gitem_t *it;
  641. // ZOID
  642. if (ent->client->menu)
  643. {
  644. PMenu_Select(ent);
  645. return;
  646. }
  647. // ZOID
  648. if (ent->health <= 0 || ent->deadflag)
  649. return;
  650. ValidateSelectedItem(ent);
  651. if (ent->client->pers.selected_item == IT_NULL)
  652. {
  653. gi.LocClient_Print(ent, PRINT_HIGH, "$g_no_item_to_use");
  654. return;
  655. }
  656. it = &itemlist[ent->client->pers.selected_item];
  657. if (!it->use)
  658. {
  659. gi.LocClient_Print(ent, PRINT_HIGH, "$g_item_not_usable");
  660. return;
  661. }
  662. // don't allow weapon chains for invuse
  663. ent->client->no_weapon_chains = true;
  664. it->use(ent, it);
  665. ValidateSelectedItem(ent);
  666. }
  667. /*
  668. =================
  669. Cmd_WeapPrev_f
  670. =================
  671. */
  672. void Cmd_WeapPrev_f(edict_t *ent)
  673. {
  674. gclient_t *cl;
  675. item_id_t i, index;
  676. gitem_t *it;
  677. item_id_t selected_weapon;
  678. cl = ent->client;
  679. if (ent->health <= 0 || ent->deadflag)
  680. return;
  681. if (!cl->pers.weapon)
  682. return;
  683. // don't allow weapon chains for weapprev
  684. cl->no_weapon_chains = true;
  685. selected_weapon = cl->pers.weapon->id;
  686. // scan for the next valid one
  687. for (i = static_cast<item_id_t>(IT_NULL + 1); i <= IT_TOTAL; i = static_cast<item_id_t>(i + 1))
  688. {
  689. // PMM - prevent scrolling through ALL weapons
  690. index = static_cast<item_id_t>((selected_weapon + IT_TOTAL - i) % IT_TOTAL);
  691. if (!cl->pers.inventory[index])
  692. continue;
  693. it = &itemlist[index];
  694. if (!it->use)
  695. continue;
  696. if (!(it->flags & IF_WEAPON))
  697. continue;
  698. it->use(ent, it);
  699. // ROGUE
  700. if (cl->newweapon == it)
  701. return; // successful
  702. // ROGUE
  703. }
  704. }
  705. /*
  706. =================
  707. Cmd_WeapNext_f
  708. =================
  709. */
  710. void Cmd_WeapNext_f(edict_t *ent)
  711. {
  712. gclient_t *cl;
  713. item_id_t i, index;
  714. gitem_t *it;
  715. item_id_t selected_weapon;
  716. cl = ent->client;
  717. if (ent->health <= 0 || ent->deadflag)
  718. return;
  719. if (!cl->pers.weapon)
  720. return;
  721. // don't allow weapon chains for weapnext
  722. cl->no_weapon_chains = true;
  723. selected_weapon = cl->pers.weapon->id;
  724. // scan for the next valid one
  725. for (i = static_cast<item_id_t>(IT_NULL + 1); i <= IT_TOTAL; i = static_cast<item_id_t>(i + 1))
  726. {
  727. // PMM - prevent scrolling through ALL weapons
  728. index = static_cast<item_id_t>((selected_weapon + i) % IT_TOTAL);
  729. if (!cl->pers.inventory[index])
  730. continue;
  731. it = &itemlist[index];
  732. if (!it->use)
  733. continue;
  734. if (!(it->flags & IF_WEAPON))
  735. continue;
  736. it->use(ent, it);
  737. // PMM - prevent scrolling through ALL weapons
  738. // ROGUE
  739. if (cl->newweapon == it)
  740. return;
  741. // ROGUE
  742. }
  743. }
  744. /*
  745. =================
  746. Cmd_WeapLast_f
  747. =================
  748. */
  749. void Cmd_WeapLast_f(edict_t *ent)
  750. {
  751. gclient_t *cl;
  752. int index;
  753. gitem_t *it;
  754. cl = ent->client;
  755. if (ent->health <= 0 || ent->deadflag)
  756. return;
  757. if (!cl->pers.weapon || !cl->pers.lastweapon)
  758. return;
  759. // don't allow weapon chains for weaplast
  760. cl->no_weapon_chains = true;
  761. index = cl->pers.lastweapon->id;
  762. if (!cl->pers.inventory[index])
  763. return;
  764. it = &itemlist[index];
  765. if (!it->use)
  766. return;
  767. if (!(it->flags & IF_WEAPON))
  768. return;
  769. it->use(ent, it);
  770. }
  771. /*
  772. =================
  773. Cmd_InvDrop_f
  774. =================
  775. */
  776. void Cmd_InvDrop_f(edict_t *ent)
  777. {
  778. gitem_t *it;
  779. if (ent->health <= 0 || ent->deadflag)
  780. return;
  781. ValidateSelectedItem(ent);
  782. if (ent->client->pers.selected_item == IT_NULL)
  783. {
  784. gi.LocClient_Print(ent, PRINT_HIGH, "$g_no_item_to_drop");
  785. return;
  786. }
  787. it = &itemlist[ent->client->pers.selected_item];
  788. if (!it->drop)
  789. {
  790. gi.LocClient_Print(ent, PRINT_HIGH, "$g_item_not_droppable");
  791. return;
  792. }
  793. it->drop(ent, it);
  794. ValidateSelectedItem(ent);
  795. }
  796. /*
  797. =================
  798. Cmd_Kill_f
  799. =================
  800. */
  801. void Cmd_Kill_f(edict_t *ent)
  802. {
  803. // ZOID
  804. if (ent->client->resp.spectator)
  805. return;
  806. // ZOID
  807. if ((level.time - ent->client->respawn_time) < 5_sec)
  808. return;
  809. ent->flags &= ~FL_GODMODE;
  810. ent->health = 0;
  811. // ROGUE
  812. // make sure no trackers are still hurting us.
  813. if (ent->client->tracker_pain_time)
  814. RemoveAttackingPainDaemons(ent);
  815. if (ent->client->owned_sphere)
  816. {
  817. G_FreeEdict(ent->client->owned_sphere);
  818. ent->client->owned_sphere = nullptr;
  819. }
  820. // ROGUE
  821. // [Paril-KEX] don't allow kill to take points away in TDM
  822. player_die(ent, ent, ent, 100000, vec3_origin, { MOD_SUICIDE, !!teamplay->integer });
  823. }
  824. /*
  825. =================
  826. Cmd_Kill_AI_f
  827. =================
  828. */
  829. void Cmd_Kill_AI_f( edict_t * ent ) {
  830. if ( !sv_cheats->integer ) {
  831. gi.LocClient_Print( ent, PRINT_HIGH, "Kill_AI: Cheats Must Be Enabled!\n" );
  832. return;
  833. }
  834. // except the one we're looking at...
  835. edict_t *looked_at = nullptr;
  836. vec3_t start = ent->s.origin + vec3_t{0.f, 0.f, (float) ent->viewheight};
  837. vec3_t end = start + ent->client->v_forward * 1024.f;
  838. looked_at = gi.traceline(start, end, ent, MASK_SHOT).ent;
  839. const int numEdicts = globals.num_edicts;
  840. for ( int edictIdx = 1; edictIdx < numEdicts; ++edictIdx )
  841. {
  842. edict_t * edict = &g_edicts[ edictIdx ];
  843. if ( !edict->inuse || edict == looked_at ) {
  844. continue;
  845. }
  846. if ( ( edict->svflags & SVF_MONSTER ) == 0 )
  847. {
  848. continue;
  849. }
  850. G_FreeEdict( edict );
  851. }
  852. gi.LocClient_Print( ent, PRINT_HIGH, "Kill_AI: All AI Are Dead...\n" );
  853. }
  854. /*
  855. =================
  856. Cmd_Where_f
  857. =================
  858. */
  859. void Cmd_Where_f( edict_t * ent ) {
  860. if ( ent == nullptr || ent->client == nullptr ) {
  861. return;
  862. }
  863. const vec3_t & origin = ent->s.origin;
  864. std::string location;
  865. fmt::format_to( std::back_inserter( location ), FMT_STRING( "{:.1f} {:.1f} {:.1f} {:.1f} {:.1f} {:.1f}\n" ), origin[ 0 ], origin[ 1 ], origin[ 2 ], ent->client->ps.viewangles[0], ent->client->ps.viewangles[1], ent->client->ps.viewangles[2] );
  866. gi.LocClient_Print( ent, PRINT_HIGH, "Location: {}\n", location.c_str() );
  867. gi.SendToClipBoard( location.c_str() );
  868. }
  869. /*
  870. =================
  871. Cmd_Clear_AI_Enemy_f
  872. =================
  873. */
  874. void Cmd_Clear_AI_Enemy_f( edict_t * ent ) {
  875. if ( !sv_cheats->integer ) {
  876. gi.LocClient_Print( ent, PRINT_HIGH, "Cmd_Clear_AI_Enemy: Cheats Must Be Enabled!\n" );
  877. return;
  878. }
  879. const int numEdicts = globals.num_edicts;
  880. for ( int edictIdx = 1; edictIdx < numEdicts; ++edictIdx ) {
  881. edict_t * edict = &g_edicts[ edictIdx ];
  882. if ( !edict->inuse ) {
  883. continue;
  884. }
  885. if ( ( edict->svflags & SVF_MONSTER ) == 0 ) {
  886. continue;
  887. }
  888. edict->monsterinfo.aiflags |= AI_FORGET_ENEMY;
  889. }
  890. gi.LocClient_Print( ent, PRINT_HIGH, "Cmd_Clear_AI_Enemy: Clear All AI Enemies...\n" );
  891. }
  892. /*
  893. =================
  894. Cmd_PutAway_f
  895. =================
  896. */
  897. void Cmd_PutAway_f(edict_t *ent)
  898. {
  899. ent->client->showscores = false;
  900. ent->client->showhelp = false;
  901. ent->client->showinventory = false;
  902. globals.server_flags &= ~SERVER_FLAG_SLOW_TIME;
  903. // ZOID
  904. if (ent->client->menu)
  905. PMenu_Close(ent);
  906. ent->client->update_chase = true;
  907. // ZOID
  908. }
  909. int PlayerSort(const void *a, const void *b)
  910. {
  911. int anum, bnum;
  912. anum = *(const int *) a;
  913. bnum = *(const int *) b;
  914. anum = game.clients[anum].ps.stats[STAT_FRAGS];
  915. bnum = game.clients[bnum].ps.stats[STAT_FRAGS];
  916. if (anum < bnum)
  917. return -1;
  918. if (anum > bnum)
  919. return 1;
  920. return 0;
  921. }
  922. constexpr size_t MAX_IDEAL_PACKET_SIZE = 1024;
  923. /*
  924. =================
  925. Cmd_Players_f
  926. =================
  927. */
  928. void Cmd_Players_f(edict_t *ent)
  929. {
  930. size_t i;
  931. size_t count;
  932. static std::string small, large;
  933. int index[MAX_CLIENTS];
  934. small.clear();
  935. large.clear();
  936. count = 0;
  937. for (i = 0; i < game.maxclients; i++)
  938. if (game.clients[i].pers.connected)
  939. {
  940. index[count] = i;
  941. count++;
  942. }
  943. // sort by frags
  944. qsort(index, count, sizeof(index[0]), PlayerSort);
  945. // print information
  946. large[0] = 0;
  947. if (count)
  948. {
  949. for (i = 0; i < count; i++)
  950. {
  951. fmt::format_to(std::back_inserter(small), FMT_STRING("{:3} {}\n"), game.clients[index[i]].ps.stats[STAT_FRAGS],
  952. game.clients[index[i]].pers.netname);
  953. if (small.length() + large.length() > MAX_IDEAL_PACKET_SIZE - 50)
  954. { // can't print all of them in one packet
  955. large += "...\n";
  956. break;
  957. }
  958. large += small;
  959. small.clear();
  960. }
  961. // remove the last newline
  962. large.pop_back();
  963. }
  964. gi.LocClient_Print(ent, PRINT_HIGH | PRINT_NO_NOTIFY, "$g_players", large.c_str(), count);
  965. }
  966. bool CheckFlood(edict_t *ent)
  967. {
  968. int i;
  969. gclient_t *cl;
  970. if (flood_msgs->integer)
  971. {
  972. cl = ent->client;
  973. if (level.time < cl->flood_locktill)
  974. {
  975. gi.LocClient_Print(ent, PRINT_HIGH, "$g_flood_cant_talk",
  976. (cl->flood_locktill - level.time).seconds<int32_t>());
  977. return true;
  978. }
  979. i = cl->flood_whenhead - flood_msgs->integer + 1;
  980. if (i < 0)
  981. i = (sizeof(cl->flood_when) / sizeof(cl->flood_when[0])) + i;
  982. if (i >= q_countof(cl->flood_when))
  983. i = 0;
  984. if (cl->flood_when[i] && level.time - cl->flood_when[i] < gtime_t::from_sec(flood_persecond->value))
  985. {
  986. cl->flood_locktill = level.time + gtime_t::from_sec(flood_waitdelay->value);
  987. gi.LocClient_Print(ent, PRINT_CHAT, "$g_flood_cant_talk",
  988. flood_waitdelay->integer);
  989. return true;
  990. }
  991. cl->flood_whenhead = (cl->flood_whenhead + 1) % (sizeof(cl->flood_when) / sizeof(cl->flood_when[0]));
  992. cl->flood_when[cl->flood_whenhead] = level.time;
  993. }
  994. return false;
  995. }
  996. /*
  997. =================
  998. Cmd_Wave_f
  999. =================
  1000. */
  1001. void Cmd_Wave_f(edict_t *ent)
  1002. {
  1003. int i;
  1004. i = atoi(gi.argv(1));
  1005. // no dead or noclip waving
  1006. if (ent->deadflag || ent->movetype == MOVETYPE_NOCLIP)
  1007. return;
  1008. // can't wave when ducked
  1009. bool do_animate = ent->client->anim_priority <= ANIM_WAVE && !(ent->client->ps.pmove.pm_flags & PMF_DUCKED);
  1010. if (do_animate)
  1011. ent->client->anim_priority = ANIM_WAVE;
  1012. const char *other_notify_msg = nullptr, *other_notify_none_msg = nullptr;
  1013. vec3_t start, dir;
  1014. P_ProjectSource(ent, ent->client->v_angle, { 0, 0, 0 }, start, dir);
  1015. // see who we're aiming at
  1016. edict_t *aiming_at = nullptr;
  1017. float best_dist = -9999;
  1018. for (auto player : active_players())
  1019. {
  1020. if (player == ent)
  1021. continue;
  1022. vec3_t cdir = player->s.origin - start;
  1023. float dist = cdir.normalize();
  1024. float dot = ent->client->v_forward.dot(cdir);
  1025. if (dot < 0.97)
  1026. continue;
  1027. else if (dist < best_dist)
  1028. continue;
  1029. best_dist = dist;
  1030. aiming_at = player;
  1031. }
  1032. switch (i)
  1033. {
  1034. case GESTURE_FLIP_OFF:
  1035. other_notify_msg = "$g_flipoff_other";
  1036. other_notify_none_msg = "$g_flipoff_none";
  1037. if (do_animate)
  1038. {
  1039. ent->s.frame = FRAME_flip01 - 1;
  1040. ent->client->anim_end = FRAME_flip12;
  1041. }
  1042. break;
  1043. case GESTURE_SALUTE:
  1044. other_notify_msg = "$g_salute_other";
  1045. other_notify_none_msg = "$g_salute_none";
  1046. if (do_animate)
  1047. {
  1048. ent->s.frame = FRAME_salute01 - 1;
  1049. ent->client->anim_end = FRAME_salute11;
  1050. }
  1051. break;
  1052. case GESTURE_TAUNT:
  1053. other_notify_msg = "$g_taunt_other";
  1054. other_notify_none_msg = "$g_taunt_none";
  1055. if (do_animate)
  1056. {
  1057. ent->s.frame = FRAME_taunt01 - 1;
  1058. ent->client->anim_end = FRAME_taunt17;
  1059. }
  1060. break;
  1061. case GESTURE_WAVE:
  1062. other_notify_msg = "$g_wave_other";
  1063. other_notify_none_msg = "$g_wave_none";
  1064. if (do_animate)
  1065. {
  1066. ent->s.frame = FRAME_wave01 - 1;
  1067. ent->client->anim_end = FRAME_wave11;
  1068. }
  1069. break;
  1070. case GESTURE_POINT:
  1071. default:
  1072. other_notify_msg = "$g_point_other";
  1073. other_notify_none_msg = "$g_point_none";
  1074. if (do_animate)
  1075. {
  1076. ent->s.frame = FRAME_point01 - 1;
  1077. ent->client->anim_end = FRAME_point12;
  1078. }
  1079. break;
  1080. }
  1081. bool has_a_target = false;
  1082. if (i == GESTURE_POINT)
  1083. {
  1084. for (auto player : active_players())
  1085. {
  1086. if (player == ent)
  1087. continue;
  1088. else if (!OnSameTeam(ent, player))
  1089. continue;
  1090. has_a_target = true;
  1091. break;
  1092. }
  1093. }
  1094. if (i == GESTURE_POINT && has_a_target)
  1095. {
  1096. // don't do this stuff if we're flooding
  1097. if (CheckFlood(ent))
  1098. return;
  1099. trace_t tr = gi.traceline(start, start + (ent->client->v_forward * 2048), ent, MASK_SHOT & ~CONTENTS_WINDOW);
  1100. other_notify_msg = "$g_point_other_ping";
  1101. uint32_t key = GetUnicastKey();
  1102. if (tr.fraction != 1.0f)
  1103. {
  1104. // send to all teammates
  1105. for (auto player : active_players())
  1106. {
  1107. if (player != ent && !OnSameTeam(ent, player))
  1108. continue;
  1109. gi.WriteByte(svc_poi);
  1110. gi.WriteShort(POI_PING + (ent->s.number - 1));
  1111. gi.WriteShort(5000);
  1112. gi.WritePosition(tr.endpos);
  1113. gi.WriteShort(level.pic_ping);
  1114. gi.WriteByte(208);
  1115. gi.WriteByte(POI_FLAG_NONE);
  1116. gi.unicast(player, false);
  1117. gi.local_sound(player, CHAN_AUTO, gi.soundindex("misc/help_marker.wav"), 1.0f, ATTN_NONE, 0.0f, key);
  1118. gi.LocClient_Print(player, PRINT_HIGH, other_notify_msg, ent->client->pers.netname);
  1119. }
  1120. }
  1121. }
  1122. else
  1123. {
  1124. if (CheckFlood(ent))
  1125. return;
  1126. edict_t* targ = nullptr;
  1127. while ((targ = findradius(targ, ent->s.origin, 1024)) != nullptr)
  1128. {
  1129. if (ent == targ) continue;
  1130. if (!targ->client) continue;
  1131. if (!gi.inPVS(ent->s.origin, targ->s.origin, false)) continue;
  1132. if (aiming_at && other_notify_msg)
  1133. gi.LocClient_Print(targ, PRINT_TTS, other_notify_msg, ent->client->pers.netname, aiming_at->client->pers.netname);
  1134. else if (other_notify_none_msg)
  1135. gi.LocClient_Print(targ, PRINT_TTS, other_notify_none_msg, ent->client->pers.netname);
  1136. }
  1137. if (aiming_at && other_notify_msg)
  1138. gi.LocClient_Print(ent, PRINT_TTS, other_notify_msg, ent->client->pers.netname, aiming_at->client->pers.netname);
  1139. else if (other_notify_none_msg)
  1140. gi.LocClient_Print(ent, PRINT_TTS, other_notify_none_msg, ent->client->pers.netname);
  1141. }
  1142. ent->client->anim_time = 0_ms;
  1143. }
  1144. #ifndef KEX_Q2_GAME
  1145. /*
  1146. ==================
  1147. Cmd_Say_f
  1148. NB: only used for non-Playfab stuff
  1149. ==================
  1150. */
  1151. void Cmd_Say_f(edict_t *ent, bool arg0)
  1152. {
  1153. edict_t *other;
  1154. const char *p_in;
  1155. static std::string text;
  1156. if (gi.argc() < 2 && !arg0)
  1157. return;
  1158. else if (CheckFlood(ent))
  1159. return;
  1160. text.clear();
  1161. fmt::format_to(std::back_inserter(text), FMT_STRING("{}: "), ent->client->pers.netname);
  1162. if (arg0)
  1163. {
  1164. text += gi.argv(0);
  1165. text += " ";
  1166. text += gi.args();
  1167. }
  1168. else
  1169. {
  1170. p_in = gi.args();
  1171. size_t in_len = strlen(p_in);
  1172. if (p_in[0] == '\"' && p_in[in_len - 1] == '\"')
  1173. text += std::string_view(p_in + 1, in_len - 2);
  1174. else
  1175. text += p_in;
  1176. }
  1177. // don't let text be too long for malicious reasons
  1178. if (text.length() > 150)
  1179. text.resize(150);
  1180. if (text.back() != '\n')
  1181. text.push_back('\n');
  1182. if (sv_dedicated->integer)
  1183. gi.Client_Print(nullptr, PRINT_CHAT, text.c_str());
  1184. for (uint32_t j = 1; j <= game.maxclients; j++)
  1185. {
  1186. other = &g_edicts[j];
  1187. if (!other->inuse)
  1188. continue;
  1189. if (!other->client)
  1190. continue;
  1191. gi.Client_Print(other, PRINT_CHAT, text.c_str());
  1192. }
  1193. }
  1194. #endif
  1195. void Cmd_PlayerList_f(edict_t *ent)
  1196. {
  1197. uint32_t i;
  1198. static std::string str, text;
  1199. edict_t *e2;
  1200. str.clear();
  1201. text.clear();
  1202. // connect time, ping, score, name
  1203. for (i = 0, e2 = g_edicts + 1; i < game.maxclients; i++, e2++)
  1204. {
  1205. if (!e2->inuse)
  1206. continue;
  1207. fmt::format_to(std::back_inserter(str), FMT_STRING("{:02}:{:02} {:4} {:3} {}{}\n"), (level.time - e2->client->resp.entertime).milliseconds() / 60000,
  1208. ((level.time - e2->client->resp.entertime).milliseconds() % 60000) / 1000, e2->client->ping,
  1209. e2->client->resp.score, e2->client->pers.netname, e2->client->resp.spectator ? " (spectator)" : "");
  1210. if (text.length() + str.length() > MAX_IDEAL_PACKET_SIZE - 50)
  1211. {
  1212. text += "...\n";
  1213. break;
  1214. }
  1215. text += str;
  1216. }
  1217. if (text.length())
  1218. gi.Client_Print(ent, PRINT_HIGH, text.c_str());
  1219. }
  1220. void Cmd_Switchteam_f(edict_t* ent)
  1221. {
  1222. if (!G_TeamplayEnabled())
  1223. return;
  1224. // [Paril-KEX] in force-join, just do a regular team join.
  1225. if (g_teamplay_force_join->integer)
  1226. {
  1227. // check if we should even switch teams
  1228. edict_t *player;
  1229. uint32_t team1count = 0, team2count = 0;
  1230. ctfteam_t best_team;
  1231. for (uint32_t i = 1; i <= game.maxclients; i++)
  1232. {
  1233. player = &g_edicts[i];
  1234. // NB: we are counting ourselves in this one, unlike
  1235. // the other assign team func
  1236. if (!player->inuse)
  1237. continue;
  1238. switch (player->client->resp.ctf_team)
  1239. {
  1240. case CTF_TEAM1:
  1241. team1count++;
  1242. break;
  1243. case CTF_TEAM2:
  1244. team2count++;
  1245. break;
  1246. default:
  1247. break;
  1248. }
  1249. }
  1250. if (team1count < team2count)
  1251. best_team = CTF_TEAM1;
  1252. else
  1253. best_team = CTF_TEAM2;
  1254. if (ent->client->resp.ctf_team != best_team)
  1255. {
  1256. ////
  1257. ent->svflags = SVF_NONE;
  1258. ent->flags &= ~FL_GODMODE;
  1259. ent->client->resp.ctf_team = best_team;
  1260. ent->client->resp.ctf_state = 0;
  1261. char value[MAX_INFO_VALUE] = { 0 };
  1262. gi.Info_ValueForKey(ent->client->pers.userinfo, "skin", value, sizeof(value));
  1263. CTFAssignSkin(ent, value);
  1264. // if anybody has a menu open, update it immediately
  1265. CTFDirtyTeamMenu();
  1266. if (ent->solid == SOLID_NOT)
  1267. {
  1268. // spectator
  1269. PutClientInServer(ent);
  1270. G_PostRespawn(ent);
  1271. gi.LocBroadcast_Print(PRINT_HIGH, "$g_joined_team",
  1272. ent->client->pers.netname, CTFTeamName(best_team));
  1273. return;
  1274. }
  1275. ent->health = 0;
  1276. player_die(ent, ent, ent, 100000, vec3_origin, { MOD_SUICIDE, true });
  1277. // don't even bother waiting for death frames
  1278. ent->deadflag = true;
  1279. respawn(ent);
  1280. ent->client->resp.score = 0;
  1281. gi.LocBroadcast_Print(PRINT_HIGH, "$g_changed_team",
  1282. ent->client->pers.netname, CTFTeamName(best_team));
  1283. }
  1284. return;
  1285. }
  1286. if (ent->client->resp.ctf_team != CTF_NOTEAM)
  1287. CTFObserver(ent);
  1288. if (!ent->client->menu)
  1289. CTFOpenJoinMenu(ent);
  1290. }
  1291. static void Cmd_ListMonsters_f(edict_t *ent)
  1292. {
  1293. if (!G_CheatCheck(ent))
  1294. return;
  1295. else if (!g_debug_monster_kills->integer)
  1296. return;
  1297. for (size_t i = 0; i < level.total_monsters; i++)
  1298. {
  1299. edict_t *e = level.monsters_registered[i];
  1300. if (!e || !e->inuse)
  1301. continue;
  1302. else if (!(e->svflags & SVF_MONSTER) || (e->monsterinfo.aiflags & AI_DO_NOT_COUNT))
  1303. continue;
  1304. else if (e->deadflag)
  1305. continue;
  1306. gi.Com_PrintFmt("{}\n", *e);
  1307. }
  1308. }
  1309. /*
  1310. =================
  1311. ClientCommand
  1312. =================
  1313. */
  1314. void ClientCommand(edict_t *ent)
  1315. {
  1316. const char *cmd;
  1317. if (!ent->client)
  1318. return; // not fully in game yet
  1319. cmd = gi.argv(0);
  1320. if (Q_strcasecmp(cmd, "players") == 0)
  1321. {
  1322. Cmd_Players_f(ent);
  1323. return;
  1324. }
  1325. // [Paril-KEX] these have to go through the lobby system
  1326. #ifndef KEX_Q2_GAME
  1327. if (Q_strcasecmp(cmd, "say") == 0)
  1328. {
  1329. Cmd_Say_f(ent, false);
  1330. return;
  1331. }
  1332. if (Q_strcasecmp(cmd, "say_team") == 0 || Q_strcasecmp(cmd, "steam") == 0)
  1333. {
  1334. if (G_TeamplayEnabled())
  1335. CTFSay_Team(ent, gi.args());
  1336. else
  1337. Cmd_Say_f(ent, false);
  1338. return;
  1339. }
  1340. #endif
  1341. if (Q_strcasecmp(cmd, "score") == 0)
  1342. {
  1343. Cmd_Score_f(ent);
  1344. return;
  1345. }
  1346. if (Q_strcasecmp(cmd, "help") == 0)
  1347. {
  1348. Cmd_Help_f(ent);
  1349. return;
  1350. }
  1351. if (Q_strcasecmp(cmd, "listmonsters") == 0)
  1352. {
  1353. Cmd_ListMonsters_f(ent);
  1354. return;
  1355. }
  1356. if (level.intermissiontime)
  1357. return;
  1358. if ( Q_strcasecmp( cmd, "target" ) == 0 )
  1359. Cmd_Target_f( ent );
  1360. else if ( Q_strcasecmp( cmd, "use" ) == 0 || Q_strcasecmp( cmd, "use_only" ) == 0 ||
  1361. Q_strcasecmp( cmd, "use_index" ) == 0 || Q_strcasecmp( cmd, "use_index_only" ) == 0 )
  1362. Cmd_Use_f( ent );
  1363. else if ( Q_strcasecmp( cmd, "drop" ) == 0 ||
  1364. Q_strcasecmp( cmd, "drop_index" ) == 0 )
  1365. Cmd_Drop_f( ent );
  1366. else if ( Q_strcasecmp( cmd, "give" ) == 0 )
  1367. Cmd_Give_f( ent );
  1368. else if ( Q_strcasecmp( cmd, "god" ) == 0 )
  1369. Cmd_God_f( ent );
  1370. else if (Q_strcasecmp(cmd, "immortal") == 0)
  1371. Cmd_Immortal_f(ent);
  1372. else if ( Q_strcasecmp( cmd, "setpoi" ) == 0 )
  1373. Cmd_SetPOI_f( ent );
  1374. else if ( Q_strcasecmp( cmd, "checkpoi" ) == 0 )
  1375. Cmd_CheckPOI_f( ent );
  1376. // Paril: cheats to help with dev
  1377. else if ( Q_strcasecmp( cmd, "spawn" ) == 0 )
  1378. Cmd_Spawn_f( ent );
  1379. else if ( Q_strcasecmp( cmd, "teleport" ) == 0 )
  1380. Cmd_Teleport_f( ent );
  1381. else if ( Q_strcasecmp( cmd, "notarget" ) == 0 )
  1382. Cmd_Notarget_f( ent );
  1383. else if ( Q_strcasecmp( cmd, "novisible" ) == 0 )
  1384. Cmd_Novisible_f( ent );
  1385. else if ( Q_strcasecmp( cmd, "alertall" ) == 0 )
  1386. Cmd_AlertAll_f( ent );
  1387. else if ( Q_strcasecmp( cmd, "noclip" ) == 0 )
  1388. Cmd_Noclip_f( ent );
  1389. else if ( Q_strcasecmp( cmd, "inven" ) == 0 )
  1390. Cmd_Inven_f( ent );
  1391. else if ( Q_strcasecmp( cmd, "invnext" ) == 0 )
  1392. SelectNextItem( ent, IF_ANY );
  1393. else if ( Q_strcasecmp( cmd, "invprev" ) == 0 )
  1394. SelectPrevItem( ent, IF_ANY );
  1395. else if ( Q_strcasecmp( cmd, "invnextw" ) == 0 )
  1396. SelectNextItem( ent, IF_WEAPON );
  1397. else if ( Q_strcasecmp( cmd, "invprevw" ) == 0 )
  1398. SelectPrevItem( ent, IF_WEAPON );
  1399. else if ( Q_strcasecmp( cmd, "invnextp" ) == 0 )
  1400. SelectNextItem( ent, IF_POWERUP );
  1401. else if ( Q_strcasecmp( cmd, "invprevp" ) == 0 )
  1402. SelectPrevItem( ent, IF_POWERUP );
  1403. else if ( Q_strcasecmp( cmd, "invuse" ) == 0 )
  1404. Cmd_InvUse_f( ent );
  1405. else if ( Q_strcasecmp( cmd, "invdrop" ) == 0 )
  1406. Cmd_InvDrop_f( ent );
  1407. else if ( Q_strcasecmp( cmd, "weapprev" ) == 0 )
  1408. Cmd_WeapPrev_f( ent );
  1409. else if ( Q_strcasecmp( cmd, "weapnext" ) == 0 )
  1410. Cmd_WeapNext_f( ent );
  1411. else if ( Q_strcasecmp( cmd, "weaplast" ) == 0 || Q_strcasecmp( cmd, "lastweap" ) == 0 )
  1412. Cmd_WeapLast_f( ent );
  1413. else if ( Q_strcasecmp( cmd, "kill" ) == 0 )
  1414. Cmd_Kill_f( ent );
  1415. else if ( Q_strcasecmp( cmd, "kill_ai" ) == 0 )
  1416. Cmd_Kill_AI_f( ent );
  1417. else if ( Q_strcasecmp( cmd, "where" ) == 0 )
  1418. Cmd_Where_f( ent );
  1419. else if ( Q_strcasecmp( cmd, "clear_ai_enemy" ) == 0 )
  1420. Cmd_Clear_AI_Enemy_f( ent );
  1421. else if (Q_strcasecmp(cmd, "putaway") == 0)
  1422. Cmd_PutAway_f(ent);
  1423. else if (Q_strcasecmp(cmd, "wave") == 0)
  1424. Cmd_Wave_f(ent);
  1425. else if (Q_strcasecmp(cmd, "playerlist") == 0)
  1426. Cmd_PlayerList_f(ent);
  1427. // ZOID
  1428. else if (Q_strcasecmp(cmd, "team") == 0)
  1429. CTFTeam_f(ent);
  1430. else if (Q_strcasecmp(cmd, "id") == 0)
  1431. CTFID_f(ent);
  1432. else if (Q_strcasecmp(cmd, "yes") == 0)
  1433. CTFVoteYes(ent);
  1434. else if (Q_strcasecmp(cmd, "no") == 0)
  1435. CTFVoteNo(ent);
  1436. else if (Q_strcasecmp(cmd, "ready") == 0)
  1437. CTFReady(ent);
  1438. else if (Q_strcasecmp(cmd, "notready") == 0)
  1439. CTFNotReady(ent);
  1440. else if (Q_strcasecmp(cmd, "ghost") == 0)
  1441. CTFGhost(ent);
  1442. else if (Q_strcasecmp(cmd, "admin") == 0)
  1443. CTFAdmin(ent);
  1444. else if (Q_strcasecmp(cmd, "stats") == 0)
  1445. CTFStats(ent);
  1446. else if (Q_strcasecmp(cmd, "warp") == 0)
  1447. CTFWarp(ent);
  1448. else if (Q_strcasecmp(cmd, "boot") == 0)
  1449. CTFBoot(ent);
  1450. else if (Q_strcasecmp(cmd, "playerlist") == 0)
  1451. CTFPlayerList(ent);
  1452. else if (Q_strcasecmp(cmd, "observer") == 0)
  1453. CTFObserver(ent);
  1454. // ZOID
  1455. else if (Q_strcasecmp(cmd, "switchteam") == 0)
  1456. Cmd_Switchteam_f(ent);
  1457. #ifndef KEX_Q2_GAME
  1458. else // anything that doesn't match a command will be a chat
  1459. Cmd_Say_f(ent, true);
  1460. #else
  1461. // anything that doesn't match a command will inform them
  1462. else
  1463. gi.LocClient_Print(ent, PRINT_HIGH, "invalid game command \"{}\"\n", gi.argv(0));
  1464. #endif
  1465. }