g_ctf.c 105 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198
  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3. This program is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU General Public License
  5. as published by the Free Software Foundation; either version 2
  6. of the License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. See the GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. */
  15. #include "g_local.h"
  16. #include "m_player.h"
  17. typedef enum match_s {
  18. MATCH_NONE,
  19. MATCH_SETUP,
  20. MATCH_PREGAME,
  21. MATCH_GAME,
  22. MATCH_POST
  23. } match_t;
  24. typedef enum {
  25. ELECT_NONE,
  26. ELECT_MATCH,
  27. ELECT_ADMIN,
  28. ELECT_MAP
  29. } elect_t;
  30. typedef struct ctfgame_s
  31. {
  32. int team1, team2;
  33. int total1, total2; // these are only set when going into intermission!
  34. float last_flag_capture;
  35. int last_capture_team;
  36. match_t match; // match state
  37. float matchtime; // time for match start/end (depends on state)
  38. int lasttime; // last time update
  39. qboolean countdown; // has audio countdown started?
  40. elect_t election; // election type
  41. edict_t *etarget; // for admin election, who's being elected
  42. char elevel[32]; // for map election, target level
  43. int evotes; // votes so far
  44. int needvotes; // votes needed
  45. float electtime; // remaining time until election times out
  46. char emsg[256]; // election name
  47. int warnactive; // true if stat string 30 is active
  48. ghost_t ghosts[MAX_CLIENTS]; // ghost codes
  49. } ctfgame_t;
  50. ctfgame_t ctfgame;
  51. cvar_t *ctf;
  52. cvar_t *ctf_forcejoin;
  53. cvar_t *competition;
  54. cvar_t *matchlock;
  55. cvar_t *electpercentage;
  56. cvar_t *matchtime;
  57. cvar_t *matchsetuptime;
  58. cvar_t *matchstarttime;
  59. cvar_t *admin_password;
  60. cvar_t *allow_admin;
  61. cvar_t *warp_list;
  62. cvar_t *warn_unbalanced;
  63. // Index for various CTF pics, this saves us from calling gi.imageindex
  64. // all the time and saves a few CPU cycles since we don't have to do
  65. // a bunch of string compares all the time.
  66. // These are set in CTFPrecache() called from worldspawn
  67. int imageindex_i_ctf1;
  68. int imageindex_i_ctf2;
  69. int imageindex_i_ctf1d;
  70. int imageindex_i_ctf2d;
  71. int imageindex_i_ctf1t;
  72. int imageindex_i_ctf2t;
  73. int imageindex_i_ctfj;
  74. int imageindex_sbfctf1;
  75. int imageindex_sbfctf2;
  76. int imageindex_ctfsb1;
  77. int imageindex_ctfsb2;
  78. char *ctf_statusbar =
  79. "yb -24 "
  80. // health
  81. "xv 0 "
  82. "hnum "
  83. "xv 50 "
  84. "pic 0 "
  85. // ammo
  86. "if 2 "
  87. " xv 100 "
  88. " anum "
  89. " xv 150 "
  90. " pic 2 "
  91. "endif "
  92. // armor
  93. "if 4 "
  94. " xv 200 "
  95. " rnum "
  96. " xv 250 "
  97. " pic 4 "
  98. "endif "
  99. // selected item
  100. "if 6 "
  101. " xv 296 "
  102. " pic 6 "
  103. "endif "
  104. "yb -50 "
  105. // picked up item
  106. "if 7 "
  107. " xv 0 "
  108. " pic 7 "
  109. " xv 26 "
  110. " yb -42 "
  111. " stat_string 8 "
  112. " yb -50 "
  113. "endif "
  114. // timer
  115. "if 9 "
  116. "xv 246 "
  117. "num 2 10 "
  118. "xv 296 "
  119. "pic 9 "
  120. "endif "
  121. // help / weapon icon
  122. "if 11 "
  123. "xv 148 "
  124. "pic 11 "
  125. "endif "
  126. // frags
  127. "xr -50 "
  128. "yt 2 "
  129. "num 3 14 "
  130. //tech
  131. "yb -129 "
  132. "if 26 "
  133. "xr -26 "
  134. "pic 26 "
  135. "endif "
  136. // red team
  137. "yb -102 "
  138. "if 17 "
  139. "xr -26 "
  140. "pic 17 "
  141. "endif "
  142. "xr -62 "
  143. "num 2 18 "
  144. //joined overlay
  145. "if 22 "
  146. "yb -104 "
  147. "xr -28 "
  148. "pic 22 "
  149. "endif "
  150. // blue team
  151. "yb -75 "
  152. "if 19 "
  153. "xr -26 "
  154. "pic 19 "
  155. "endif "
  156. "xr -62 "
  157. "num 2 20 "
  158. "if 23 "
  159. "yb -77 "
  160. "xr -28 "
  161. "pic 23 "
  162. "endif "
  163. // have flag graph
  164. "if 21 "
  165. "yt 26 "
  166. "xr -24 "
  167. "pic 21 "
  168. "endif "
  169. // id view state
  170. "if 27 "
  171. "xv 112 "
  172. "yb -58 "
  173. "stat_string 27 "
  174. "endif "
  175. "if 29 "
  176. "xv 96 "
  177. "yb -58 "
  178. "pic 29 "
  179. "endif "
  180. "if 28 "
  181. "xl 0 "
  182. "yb -78 "
  183. "stat_string 28 "
  184. "endif "
  185. "if 30 "
  186. "xl 0 "
  187. "yb -88 "
  188. "stat_string 30 "
  189. "endif "
  190. ;
  191. static char *tnames[] = {
  192. "item_tech1", "item_tech2", "item_tech3", "item_tech4",
  193. NULL
  194. };
  195. void stuffcmd(edict_t *ent, char *s)
  196. {
  197. gi.WriteByte (11);
  198. gi.WriteString (s);
  199. gi.unicast (ent, true);
  200. }
  201. /*--------------------------------------------------------------------------*/
  202. /*
  203. =================
  204. findradius
  205. Returns entities that have origins within a spherical area
  206. findradius (origin, radius)
  207. =================
  208. */
  209. static edict_t *loc_findradius (edict_t *from, vec3_t org, float rad)
  210. {
  211. vec3_t eorg;
  212. int j;
  213. if (!from)
  214. from = g_edicts;
  215. else
  216. from++;
  217. for ( ; from < &g_edicts[globals.num_edicts]; from++)
  218. {
  219. if (!from->inuse)
  220. continue;
  221. #if 0
  222. if (from->solid == SOLID_NOT)
  223. continue;
  224. #endif
  225. for (j=0 ; j<3 ; j++)
  226. eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
  227. if (VectorLength(eorg) > rad)
  228. continue;
  229. return from;
  230. }
  231. return NULL;
  232. }
  233. static void loc_buildboxpoints(vec3_t p[8], vec3_t org, vec3_t mins, vec3_t maxs)
  234. {
  235. VectorAdd(org, mins, p[0]);
  236. VectorCopy(p[0], p[1]);
  237. p[1][0] -= mins[0];
  238. VectorCopy(p[0], p[2]);
  239. p[2][1] -= mins[1];
  240. VectorCopy(p[0], p[3]);
  241. p[3][0] -= mins[0];
  242. p[3][1] -= mins[1];
  243. VectorAdd(org, maxs, p[4]);
  244. VectorCopy(p[4], p[5]);
  245. p[5][0] -= maxs[0];
  246. VectorCopy(p[0], p[6]);
  247. p[6][1] -= maxs[1];
  248. VectorCopy(p[0], p[7]);
  249. p[7][0] -= maxs[0];
  250. p[7][1] -= maxs[1];
  251. }
  252. static qboolean loc_CanSee (edict_t *targ, edict_t *inflictor)
  253. {
  254. trace_t trace;
  255. vec3_t targpoints[8];
  256. int i;
  257. vec3_t viewpoint;
  258. // bmodels need special checking because their origin is 0,0,0
  259. if (targ->movetype == MOVETYPE_PUSH)
  260. return false; // bmodels not supported
  261. loc_buildboxpoints(targpoints, targ->s.origin, targ->mins, targ->maxs);
  262. VectorCopy(inflictor->s.origin, viewpoint);
  263. viewpoint[2] += inflictor->viewheight;
  264. for (i = 0; i < 8; i++) {
  265. trace = gi.trace (viewpoint, vec3_origin, vec3_origin, targpoints[i], inflictor, MASK_SOLID);
  266. if (trace.fraction == 1.0)
  267. return true;
  268. }
  269. return false;
  270. }
  271. /*--------------------------------------------------------------------------*/
  272. static gitem_t *flag1_item;
  273. static gitem_t *flag2_item;
  274. void CTFSpawn(void)
  275. {
  276. if (!flag1_item)
  277. flag1_item = FindItemByClassname("item_flag_team1");
  278. if (!flag2_item)
  279. flag2_item = FindItemByClassname("item_flag_team2");
  280. memset(&ctfgame, 0, sizeof(ctfgame));
  281. CTFSetupTechSpawn();
  282. if (competition->value > 1) {
  283. ctfgame.match = MATCH_SETUP;
  284. ctfgame.matchtime = level.time + matchsetuptime->value * 60;
  285. }
  286. }
  287. void CTFInit(void)
  288. {
  289. ctf = gi.cvar("ctf", "1", CVAR_SERVERINFO);
  290. ctf_forcejoin = gi.cvar("ctf_forcejoin", "", 0);
  291. competition = gi.cvar("competition", "0", CVAR_SERVERINFO);
  292. matchlock = gi.cvar("matchlock", "1", CVAR_SERVERINFO);
  293. electpercentage = gi.cvar("electpercentage", "66", 0);
  294. matchtime = gi.cvar("matchtime", "20", CVAR_SERVERINFO);
  295. matchsetuptime = gi.cvar("matchsetuptime", "10", 0);
  296. matchstarttime = gi.cvar("matchstarttime", "20", 0);
  297. admin_password = gi.cvar("admin_password", "", 0);
  298. allow_admin = gi.cvar("allow_admin", "1", 0);
  299. warp_list = gi.cvar("warp_list", "q2ctf1 q2ctf2 q2ctf3 q2ctf4 q2ctf5", 0);
  300. warn_unbalanced = gi.cvar("warn_unbalanced", "1", 0);
  301. }
  302. /*
  303. * Precache CTF items
  304. */
  305. void CTFPrecache(void)
  306. {
  307. imageindex_i_ctf1 = gi.imageindex("i_ctf1");
  308. imageindex_i_ctf2 = gi.imageindex("i_ctf2");
  309. imageindex_i_ctf1d = gi.imageindex("i_ctf1d");
  310. imageindex_i_ctf2d = gi.imageindex("i_ctf2d");
  311. imageindex_i_ctf1t = gi.imageindex("i_ctf1t");
  312. imageindex_i_ctf2t = gi.imageindex("i_ctf2t");
  313. imageindex_i_ctfj = gi.imageindex("i_ctfj");
  314. imageindex_sbfctf1 = gi.imageindex("sbfctf1");
  315. imageindex_sbfctf2 = gi.imageindex("sbfctf2");
  316. imageindex_ctfsb1 = gi.imageindex("ctfsb1");
  317. imageindex_ctfsb2 = gi.imageindex("ctfsb2");
  318. }
  319. /*--------------------------------------------------------------------------*/
  320. char *CTFTeamName(int team)
  321. {
  322. switch (team) {
  323. case CTF_TEAM1:
  324. return "RED";
  325. case CTF_TEAM2:
  326. return "BLUE";
  327. }
  328. return "UNKNOWN"; // Hanzo pointed out this was spelled wrong as "UKNOWN"
  329. }
  330. char *CTFOtherTeamName(int team)
  331. {
  332. switch (team) {
  333. case CTF_TEAM1:
  334. return "BLUE";
  335. case CTF_TEAM2:
  336. return "RED";
  337. }
  338. return "UNKNOWN"; // Hanzo pointed out this was spelled wrong as "UKNOWN"
  339. }
  340. int CTFOtherTeam(int team)
  341. {
  342. switch (team) {
  343. case CTF_TEAM1:
  344. return CTF_TEAM2;
  345. case CTF_TEAM2:
  346. return CTF_TEAM1;
  347. }
  348. return -1; // invalid value
  349. }
  350. /*--------------------------------------------------------------------------*/
  351. edict_t *SelectRandomDeathmatchSpawnPoint (void);
  352. edict_t *SelectFarthestDeathmatchSpawnPoint (void);
  353. float PlayersRangeFromSpot (edict_t *spot);
  354. void CTFAssignSkin(edict_t *ent, char *s)
  355. {
  356. int playernum = ent-g_edicts-1;
  357. char *p;
  358. char t[64];
  359. Com_sprintf(t, sizeof(t), "%s", s);
  360. if ((p = strchr(t, '/')) != NULL)
  361. p[1] = 0;
  362. else
  363. strcpy(t, "male/");
  364. switch (ent->client->resp.ctf_team) {
  365. case CTF_TEAM1:
  366. gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s%s",
  367. ent->client->pers.netname, t, CTF_TEAM1_SKIN) );
  368. break;
  369. case CTF_TEAM2:
  370. gi.configstring (CS_PLAYERSKINS+playernum,
  371. va("%s\\%s%s", ent->client->pers.netname, t, CTF_TEAM2_SKIN) );
  372. break;
  373. default:
  374. gi.configstring (CS_PLAYERSKINS+playernum,
  375. va("%s\\%s", ent->client->pers.netname, s) );
  376. break;
  377. }
  378. // gi.cprintf(ent, PRINT_HIGH, "You have been assigned to %s team.\n", ent->client->pers.netname);
  379. }
  380. void CTFAssignTeam(gclient_t *who)
  381. {
  382. edict_t *player;
  383. int i;
  384. int team1count = 0, team2count = 0;
  385. who->resp.ctf_state = 0;
  386. if (!((int)dmflags->value & DF_CTF_FORCEJOIN)) {
  387. who->resp.ctf_team = CTF_NOTEAM;
  388. return;
  389. }
  390. for (i = 1; i <= maxclients->value; i++) {
  391. player = &g_edicts[i];
  392. if (!player->inuse || player->client == who)
  393. continue;
  394. switch (player->client->resp.ctf_team) {
  395. case CTF_TEAM1:
  396. team1count++;
  397. break;
  398. case CTF_TEAM2:
  399. team2count++;
  400. }
  401. }
  402. if (team1count < team2count)
  403. who->resp.ctf_team = CTF_TEAM1;
  404. else if (team2count < team1count)
  405. who->resp.ctf_team = CTF_TEAM2;
  406. else if (rand() & 1)
  407. who->resp.ctf_team = CTF_TEAM1;
  408. else
  409. who->resp.ctf_team = CTF_TEAM2;
  410. }
  411. /*
  412. ================
  413. SelectCTFSpawnPoint
  414. go to a ctf point, but NOT the two points closest
  415. to other players
  416. ================
  417. */
  418. edict_t *SelectCTFSpawnPoint (edict_t *ent)
  419. {
  420. edict_t *spot, *spot1, *spot2;
  421. int count = 0;
  422. int selection;
  423. float range, range1, range2;
  424. char *cname;
  425. if (ent->client->resp.ctf_state)
  426. if ( (int)(dmflags->value) & DF_SPAWN_FARTHEST)
  427. return SelectFarthestDeathmatchSpawnPoint ();
  428. else
  429. return SelectRandomDeathmatchSpawnPoint ();
  430. ent->client->resp.ctf_state++;
  431. switch (ent->client->resp.ctf_team) {
  432. case CTF_TEAM1:
  433. cname = "info_player_team1";
  434. break;
  435. case CTF_TEAM2:
  436. cname = "info_player_team2";
  437. break;
  438. default:
  439. return SelectRandomDeathmatchSpawnPoint();
  440. }
  441. spot = NULL;
  442. range1 = range2 = 99999;
  443. spot1 = spot2 = NULL;
  444. while ((spot = G_Find (spot, FOFS(classname), cname)) != NULL)
  445. {
  446. count++;
  447. range = PlayersRangeFromSpot(spot);
  448. if (range < range1)
  449. {
  450. range1 = range;
  451. spot1 = spot;
  452. }
  453. else if (range < range2)
  454. {
  455. range2 = range;
  456. spot2 = spot;
  457. }
  458. }
  459. if (!count)
  460. return SelectRandomDeathmatchSpawnPoint();
  461. if (count <= 2)
  462. {
  463. spot1 = spot2 = NULL;
  464. }
  465. else
  466. count -= 2;
  467. selection = rand() % count;
  468. spot = NULL;
  469. do
  470. {
  471. spot = G_Find (spot, FOFS(classname), cname);
  472. if (spot == spot1 || spot == spot2)
  473. selection++;
  474. } while(selection--);
  475. return spot;
  476. }
  477. /*------------------------------------------------------------------------*/
  478. /*
  479. CTFFragBonuses
  480. Calculate the bonuses for flag defense, flag carrier defense, etc.
  481. Note that bonuses are not cumaltive. You get one, they are in importance
  482. order.
  483. */
  484. void CTFFragBonuses(edict_t *targ, edict_t *inflictor, edict_t *attacker)
  485. {
  486. int i;
  487. edict_t *ent;
  488. gitem_t *flag_item, *enemy_flag_item;
  489. int otherteam;
  490. edict_t *flag, *carrier;
  491. char *c;
  492. vec3_t v1, v2;
  493. if (targ->client && attacker->client) {
  494. if (attacker->client->resp.ghost)
  495. if (attacker != targ)
  496. attacker->client->resp.ghost->kills++;
  497. if (targ->client->resp.ghost)
  498. targ->client->resp.ghost->deaths++;
  499. }
  500. // no bonus for fragging yourself
  501. if (!targ->client || !attacker->client || targ == attacker)
  502. return;
  503. otherteam = CTFOtherTeam(targ->client->resp.ctf_team);
  504. if (otherteam < 0)
  505. return; // whoever died isn't on a team
  506. // same team, if the flag at base, check to he has the enemy flag
  507. if (targ->client->resp.ctf_team == CTF_TEAM1) {
  508. flag_item = flag1_item;
  509. enemy_flag_item = flag2_item;
  510. } else {
  511. flag_item = flag2_item;
  512. enemy_flag_item = flag1_item;
  513. }
  514. // did the attacker frag the flag carrier?
  515. if (targ->client->pers.inventory[ITEM_INDEX(enemy_flag_item)]) {
  516. attacker->client->resp.ctf_lastfraggedcarrier = level.time;
  517. attacker->client->resp.score += CTF_FRAG_CARRIER_BONUS;
  518. gi.cprintf(attacker, PRINT_MEDIUM, "BONUS: %d points for fragging enemy flag carrier.\n",
  519. CTF_FRAG_CARRIER_BONUS);
  520. // the target had the flag, clear the hurt carrier
  521. // field on the other team
  522. for (i = 1; i <= maxclients->value; i++) {
  523. ent = g_edicts + i;
  524. if (ent->inuse && ent->client->resp.ctf_team == otherteam)
  525. ent->client->resp.ctf_lasthurtcarrier = 0;
  526. }
  527. return;
  528. }
  529. if (targ->client->resp.ctf_lasthurtcarrier &&
  530. level.time - targ->client->resp.ctf_lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT &&
  531. !attacker->client->pers.inventory[ITEM_INDEX(flag_item)]) {
  532. // attacker is on the same team as the flag carrier and
  533. // fragged a guy who hurt our flag carrier
  534. attacker->client->resp.score += CTF_CARRIER_DANGER_PROTECT_BONUS;
  535. gi.bprintf(PRINT_MEDIUM, "%s defends %s's flag carrier against an agressive enemy\n",
  536. attacker->client->pers.netname,
  537. CTFTeamName(attacker->client->resp.ctf_team));
  538. if (attacker->client->resp.ghost)
  539. attacker->client->resp.ghost->carrierdef++;
  540. return;
  541. }
  542. // flag and flag carrier area defense bonuses
  543. // we have to find the flag and carrier entities
  544. // find the flag
  545. switch (attacker->client->resp.ctf_team) {
  546. case CTF_TEAM1:
  547. c = "item_flag_team1";
  548. break;
  549. case CTF_TEAM2:
  550. c = "item_flag_team2";
  551. break;
  552. default:
  553. return;
  554. }
  555. flag = NULL;
  556. while ((flag = G_Find (flag, FOFS(classname), c)) != NULL) {
  557. if (!(flag->spawnflags & DROPPED_ITEM))
  558. break;
  559. }
  560. if (!flag)
  561. return; // can't find attacker's flag
  562. // find attacker's team's flag carrier
  563. for (i = 1; i <= maxclients->value; i++) {
  564. carrier = g_edicts + i;
  565. if (carrier->inuse &&
  566. carrier->client->pers.inventory[ITEM_INDEX(flag_item)])
  567. break;
  568. carrier = NULL;
  569. }
  570. // ok we have the attackers flag and a pointer to the carrier
  571. // check to see if we are defending the base's flag
  572. VectorSubtract(targ->s.origin, flag->s.origin, v1);
  573. VectorSubtract(attacker->s.origin, flag->s.origin, v2);
  574. if ((VectorLength(v1) < CTF_TARGET_PROTECT_RADIUS ||
  575. VectorLength(v2) < CTF_TARGET_PROTECT_RADIUS ||
  576. loc_CanSee(flag, targ) || loc_CanSee(flag, attacker)) &&
  577. attacker->client->resp.ctf_team != targ->client->resp.ctf_team) {
  578. // we defended the base flag
  579. attacker->client->resp.score += CTF_FLAG_DEFENSE_BONUS;
  580. if (flag->solid == SOLID_NOT)
  581. gi.bprintf(PRINT_MEDIUM, "%s defends the %s base.\n",
  582. attacker->client->pers.netname,
  583. CTFTeamName(attacker->client->resp.ctf_team));
  584. else
  585. gi.bprintf(PRINT_MEDIUM, "%s defends the %s flag.\n",
  586. attacker->client->pers.netname,
  587. CTFTeamName(attacker->client->resp.ctf_team));
  588. if (attacker->client->resp.ghost)
  589. attacker->client->resp.ghost->basedef++;
  590. return;
  591. }
  592. if (carrier && carrier != attacker) {
  593. VectorSubtract(targ->s.origin, carrier->s.origin, v1);
  594. VectorSubtract(attacker->s.origin, carrier->s.origin, v1);
  595. if (VectorLength(v1) < CTF_ATTACKER_PROTECT_RADIUS ||
  596. VectorLength(v2) < CTF_ATTACKER_PROTECT_RADIUS ||
  597. loc_CanSee(carrier, targ) || loc_CanSee(carrier, attacker)) {
  598. attacker->client->resp.score += CTF_CARRIER_PROTECT_BONUS;
  599. gi.bprintf(PRINT_MEDIUM, "%s defends the %s's flag carrier.\n",
  600. attacker->client->pers.netname,
  601. CTFTeamName(attacker->client->resp.ctf_team));
  602. if (attacker->client->resp.ghost)
  603. attacker->client->resp.ghost->carrierdef++;
  604. return;
  605. }
  606. }
  607. }
  608. void CTFCheckHurtCarrier(edict_t *targ, edict_t *attacker)
  609. {
  610. gitem_t *flag_item;
  611. if (!targ->client || !attacker->client)
  612. return;
  613. if (targ->client->resp.ctf_team == CTF_TEAM1)
  614. flag_item = flag2_item;
  615. else
  616. flag_item = flag1_item;
  617. if (targ->client->pers.inventory[ITEM_INDEX(flag_item)] &&
  618. targ->client->resp.ctf_team != attacker->client->resp.ctf_team)
  619. attacker->client->resp.ctf_lasthurtcarrier = level.time;
  620. }
  621. /*------------------------------------------------------------------------*/
  622. void CTFResetFlag(int ctf_team)
  623. {
  624. char *c;
  625. edict_t *ent;
  626. switch (ctf_team) {
  627. case CTF_TEAM1:
  628. c = "item_flag_team1";
  629. break;
  630. case CTF_TEAM2:
  631. c = "item_flag_team2";
  632. break;
  633. default:
  634. return;
  635. }
  636. ent = NULL;
  637. while ((ent = G_Find (ent, FOFS(classname), c)) != NULL) {
  638. if (ent->spawnflags & DROPPED_ITEM)
  639. G_FreeEdict(ent);
  640. else {
  641. ent->svflags &= ~SVF_NOCLIENT;
  642. ent->solid = SOLID_TRIGGER;
  643. gi.linkentity(ent);
  644. ent->s.event = EV_ITEM_RESPAWN;
  645. }
  646. }
  647. }
  648. void CTFResetFlags(void)
  649. {
  650. CTFResetFlag(CTF_TEAM1);
  651. CTFResetFlag(CTF_TEAM2);
  652. }
  653. qboolean CTFPickup_Flag(edict_t *ent, edict_t *other)
  654. {
  655. int ctf_team;
  656. int i;
  657. edict_t *player;
  658. gitem_t *flag_item, *enemy_flag_item;
  659. // figure out what team this flag is
  660. if (strcmp(ent->classname, "item_flag_team1") == 0)
  661. ctf_team = CTF_TEAM1;
  662. else if (strcmp(ent->classname, "item_flag_team2") == 0)
  663. ctf_team = CTF_TEAM2;
  664. else {
  665. gi.cprintf(ent, PRINT_HIGH, "Don't know what team the flag is on.\n");
  666. return false;
  667. }
  668. // same team, if the flag at base, check to he has the enemy flag
  669. if (ctf_team == CTF_TEAM1) {
  670. flag_item = flag1_item;
  671. enemy_flag_item = flag2_item;
  672. } else {
  673. flag_item = flag2_item;
  674. enemy_flag_item = flag1_item;
  675. }
  676. if (ctf_team == other->client->resp.ctf_team) {
  677. if (!(ent->spawnflags & DROPPED_ITEM)) {
  678. // the flag is at home base. if the player has the enemy
  679. // flag, he's just won!
  680. if (other->client->pers.inventory[ITEM_INDEX(enemy_flag_item)]) {
  681. gi.bprintf(PRINT_HIGH, "%s captured the %s flag!\n",
  682. other->client->pers.netname, CTFOtherTeamName(ctf_team));
  683. other->client->pers.inventory[ITEM_INDEX(enemy_flag_item)] = 0;
  684. ctfgame.last_flag_capture = level.time;
  685. ctfgame.last_capture_team = ctf_team;
  686. if (ctf_team == CTF_TEAM1)
  687. ctfgame.team1++;
  688. else
  689. ctfgame.team2++;
  690. gi.sound (ent, CHAN_RELIABLE+CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex("ctf/flagcap.wav"), 1, ATTN_NONE, 0);
  691. // other gets another 10 frag bonus
  692. other->client->resp.score += CTF_CAPTURE_BONUS;
  693. if (other->client->resp.ghost)
  694. other->client->resp.ghost->caps++;
  695. // Ok, let's do the player loop, hand out the bonuses
  696. for (i = 1; i <= maxclients->value; i++) {
  697. player = &g_edicts[i];
  698. if (!player->inuse)
  699. continue;
  700. if (player->client->resp.ctf_team != other->client->resp.ctf_team)
  701. player->client->resp.ctf_lasthurtcarrier = -5;
  702. else if (player->client->resp.ctf_team == other->client->resp.ctf_team) {
  703. if (player != other)
  704. player->client->resp.score += CTF_TEAM_BONUS;
  705. // award extra points for capture assists
  706. if (player->client->resp.ctf_lastreturnedflag + CTF_RETURN_FLAG_ASSIST_TIMEOUT > level.time) {
  707. gi.bprintf(PRINT_HIGH, "%s gets an assist for returning the flag!\n", player->client->pers.netname);
  708. player->client->resp.score += CTF_RETURN_FLAG_ASSIST_BONUS;
  709. }
  710. if (player->client->resp.ctf_lastfraggedcarrier + CTF_FRAG_CARRIER_ASSIST_TIMEOUT > level.time) {
  711. gi.bprintf(PRINT_HIGH, "%s gets an assist for fragging the flag carrier!\n", player->client->pers.netname);
  712. player->client->resp.score += CTF_FRAG_CARRIER_ASSIST_BONUS;
  713. }
  714. }
  715. }
  716. CTFResetFlags();
  717. return false;
  718. }
  719. return false; // its at home base already
  720. }
  721. // hey, its not home. return it by teleporting it back
  722. gi.bprintf(PRINT_HIGH, "%s returned the %s flag!\n",
  723. other->client->pers.netname, CTFTeamName(ctf_team));
  724. other->client->resp.score += CTF_RECOVERY_BONUS;
  725. other->client->resp.ctf_lastreturnedflag = level.time;
  726. gi.sound (ent, CHAN_RELIABLE+CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex("ctf/flagret.wav"), 1, ATTN_NONE, 0);
  727. //CTFResetFlag will remove this entity! We must return false
  728. CTFResetFlag(ctf_team);
  729. return false;
  730. }
  731. // hey, its not our flag, pick it up
  732. gi.bprintf(PRINT_HIGH, "%s got the %s flag!\n",
  733. other->client->pers.netname, CTFTeamName(ctf_team));
  734. other->client->resp.score += CTF_FLAG_BONUS;
  735. other->client->pers.inventory[ITEM_INDEX(flag_item)] = 1;
  736. other->client->resp.ctf_flagsince = level.time;
  737. // pick up the flag
  738. // if it's not a dropped flag, we just make is disappear
  739. // if it's dropped, it will be removed by the pickup caller
  740. if (!(ent->spawnflags & DROPPED_ITEM)) {
  741. ent->flags |= FL_RESPAWN;
  742. ent->svflags |= SVF_NOCLIENT;
  743. ent->solid = SOLID_NOT;
  744. }
  745. return true;
  746. }
  747. static void CTFDropFlagTouch(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  748. {
  749. //owner (who dropped us) can't touch for two secs
  750. if (other == ent->owner &&
  751. ent->nextthink - level.time > CTF_AUTO_FLAG_RETURN_TIMEOUT-2)
  752. return;
  753. Touch_Item (ent, other, plane, surf);
  754. }
  755. static void CTFDropFlagThink(edict_t *ent)
  756. {
  757. // auto return the flag
  758. // reset flag will remove ourselves
  759. if (strcmp(ent->classname, "item_flag_team1") == 0) {
  760. CTFResetFlag(CTF_TEAM1);
  761. gi.bprintf(PRINT_HIGH, "The %s flag has returned!\n",
  762. CTFTeamName(CTF_TEAM1));
  763. } else if (strcmp(ent->classname, "item_flag_team2") == 0) {
  764. CTFResetFlag(CTF_TEAM2);
  765. gi.bprintf(PRINT_HIGH, "The %s flag has returned!\n",
  766. CTFTeamName(CTF_TEAM2));
  767. }
  768. }
  769. // Called from PlayerDie, to drop the flag from a dying player
  770. void CTFDeadDropFlag(edict_t *self)
  771. {
  772. edict_t *dropped = NULL;
  773. if (self->client->pers.inventory[ITEM_INDEX(flag1_item)]) {
  774. dropped = Drop_Item(self, flag1_item);
  775. self->client->pers.inventory[ITEM_INDEX(flag1_item)] = 0;
  776. gi.bprintf(PRINT_HIGH, "%s lost the %s flag!\n",
  777. self->client->pers.netname, CTFTeamName(CTF_TEAM1));
  778. } else if (self->client->pers.inventory[ITEM_INDEX(flag2_item)]) {
  779. dropped = Drop_Item(self, flag2_item);
  780. self->client->pers.inventory[ITEM_INDEX(flag2_item)] = 0;
  781. gi.bprintf(PRINT_HIGH, "%s lost the %s flag!\n",
  782. self->client->pers.netname, CTFTeamName(CTF_TEAM2));
  783. }
  784. if (dropped) {
  785. dropped->think = CTFDropFlagThink;
  786. dropped->nextthink = level.time + CTF_AUTO_FLAG_RETURN_TIMEOUT;
  787. dropped->touch = CTFDropFlagTouch;
  788. }
  789. }
  790. qboolean CTFDrop_Flag(edict_t *ent, gitem_t *item)
  791. {
  792. if (rand() & 1)
  793. gi.cprintf(ent, PRINT_HIGH, "Only lusers drop flags.\n");
  794. else
  795. gi.cprintf(ent, PRINT_HIGH, "Winners don't drop flags.\n");
  796. return false;
  797. }
  798. static void CTFFlagThink(edict_t *ent)
  799. {
  800. if (ent->solid != SOLID_NOT)
  801. ent->s.frame = 173 + (((ent->s.frame - 173) + 1) % 16);
  802. ent->nextthink = level.time + FRAMETIME;
  803. }
  804. void CTFFlagSetup (edict_t *ent)
  805. {
  806. trace_t tr;
  807. vec3_t dest;
  808. float *v;
  809. v = tv(-15,-15,-15);
  810. VectorCopy (v, ent->mins);
  811. v = tv(15,15,15);
  812. VectorCopy (v, ent->maxs);
  813. if (ent->model)
  814. gi.setmodel (ent, ent->model);
  815. else
  816. gi.setmodel (ent, ent->item->world_model);
  817. ent->solid = SOLID_TRIGGER;
  818. ent->movetype = MOVETYPE_TOSS;
  819. ent->touch = Touch_Item;
  820. v = tv(0,0,-128);
  821. VectorAdd (ent->s.origin, v, dest);
  822. tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, dest, ent, MASK_SOLID);
  823. if (tr.startsolid)
  824. {
  825. gi.dprintf ("CTFFlagSetup: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin));
  826. G_FreeEdict (ent);
  827. return;
  828. }
  829. VectorCopy (tr.endpos, ent->s.origin);
  830. gi.linkentity (ent);
  831. ent->nextthink = level.time + FRAMETIME;
  832. ent->think = CTFFlagThink;
  833. }
  834. void CTFEffects(edict_t *player)
  835. {
  836. player->s.effects &= ~(EF_FLAG1 | EF_FLAG2);
  837. if (player->health > 0) {
  838. if (player->client->pers.inventory[ITEM_INDEX(flag1_item)]) {
  839. player->s.effects |= EF_FLAG1;
  840. }
  841. if (player->client->pers.inventory[ITEM_INDEX(flag2_item)]) {
  842. player->s.effects |= EF_FLAG2;
  843. }
  844. }
  845. if (player->client->pers.inventory[ITEM_INDEX(flag1_item)])
  846. player->s.modelindex3 = gi.modelindex("players/male/flag1.md2");
  847. else if (player->client->pers.inventory[ITEM_INDEX(flag2_item)])
  848. player->s.modelindex3 = gi.modelindex("players/male/flag2.md2");
  849. else
  850. player->s.modelindex3 = 0;
  851. }
  852. // called when we enter the intermission
  853. void CTFCalcScores(void)
  854. {
  855. int i;
  856. ctfgame.total1 = ctfgame.total2 = 0;
  857. for (i = 0; i < maxclients->value; i++) {
  858. if (!g_edicts[i+1].inuse)
  859. continue;
  860. if (game.clients[i].resp.ctf_team == CTF_TEAM1)
  861. ctfgame.total1 += game.clients[i].resp.score;
  862. else if (game.clients[i].resp.ctf_team == CTF_TEAM2)
  863. ctfgame.total2 += game.clients[i].resp.score;
  864. }
  865. }
  866. void CTFID_f (edict_t *ent)
  867. {
  868. if (ent->client->resp.id_state) {
  869. gi.cprintf(ent, PRINT_HIGH, "Disabling player identication display.\n");
  870. ent->client->resp.id_state = false;
  871. } else {
  872. gi.cprintf(ent, PRINT_HIGH, "Activating player identication display.\n");
  873. ent->client->resp.id_state = true;
  874. }
  875. }
  876. static void CTFSetIDView(edict_t *ent)
  877. {
  878. vec3_t forward, dir;
  879. trace_t tr;
  880. edict_t *who, *best;
  881. float bd = 0, d;
  882. int i;
  883. // only check every few frames
  884. if (level.time - ent->client->resp.lastidtime < 0.25)
  885. return;
  886. ent->client->resp.lastidtime = level.time;
  887. ent->client->ps.stats[STAT_CTF_ID_VIEW] = 0;
  888. ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = 0;
  889. AngleVectors(ent->client->v_angle, forward, NULL, NULL);
  890. VectorScale(forward, 1024, forward);
  891. VectorAdd(ent->s.origin, forward, forward);
  892. tr = gi.trace(ent->s.origin, NULL, NULL, forward, ent, MASK_SOLID);
  893. if (tr.fraction < 1 && tr.ent && tr.ent->client) {
  894. ent->client->ps.stats[STAT_CTF_ID_VIEW] =
  895. CS_GENERAL + (tr.ent - g_edicts - 1);
  896. if (tr.ent->client->resp.ctf_team == CTF_TEAM1)
  897. ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = imageindex_sbfctf1;
  898. else if (tr.ent->client->resp.ctf_team == CTF_TEAM2)
  899. ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = imageindex_sbfctf2;
  900. return;
  901. }
  902. AngleVectors(ent->client->v_angle, forward, NULL, NULL);
  903. best = NULL;
  904. for (i = 1; i <= maxclients->value; i++) {
  905. who = g_edicts + i;
  906. if (!who->inuse || who->solid == SOLID_NOT)
  907. continue;
  908. VectorSubtract(who->s.origin, ent->s.origin, dir);
  909. VectorNormalize(dir);
  910. d = DotProduct(forward, dir);
  911. if (d > bd && loc_CanSee(ent, who)) {
  912. bd = d;
  913. best = who;
  914. }
  915. }
  916. if (bd > 0.90) {
  917. ent->client->ps.stats[STAT_CTF_ID_VIEW] =
  918. CS_GENERAL + (best - g_edicts - 1);
  919. if (best->client->resp.ctf_team == CTF_TEAM1)
  920. ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = imageindex_sbfctf1;
  921. else if (best->client->resp.ctf_team == CTF_TEAM2)
  922. ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = imageindex_sbfctf2;
  923. }
  924. }
  925. void SetCTFStats(edict_t *ent)
  926. {
  927. gitem_t *tech;
  928. int i;
  929. int p1, p2;
  930. edict_t *e;
  931. if (ctfgame.match > MATCH_NONE)
  932. ent->client->ps.stats[STAT_CTF_MATCH] = CONFIG_CTF_MATCH;
  933. else
  934. ent->client->ps.stats[STAT_CTF_MATCH] = 0;
  935. if (ctfgame.warnactive)
  936. ent->client->ps.stats[STAT_CTF_TEAMINFO] = CONFIG_CTF_TEAMINFO;
  937. else
  938. ent->client->ps.stats[STAT_CTF_TEAMINFO] = 0;
  939. //ghosting
  940. if (ent->client->resp.ghost) {
  941. ent->client->resp.ghost->score = ent->client->resp.score;
  942. strcpy(ent->client->resp.ghost->netname, ent->client->pers.netname);
  943. ent->client->resp.ghost->number = ent->s.number;
  944. }
  945. // logo headers for the frag display
  946. ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = imageindex_ctfsb1;
  947. ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = imageindex_ctfsb2;
  948. // if during intermission, we must blink the team header of the winning team
  949. if (level.intermissiontime && (level.framenum & 8)) { // blink 1/8th second
  950. // note that ctfgame.total[12] is set when we go to intermission
  951. if (ctfgame.team1 > ctfgame.team2)
  952. ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0;
  953. else if (ctfgame.team2 > ctfgame.team1)
  954. ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
  955. else if (ctfgame.total1 > ctfgame.total2) // frag tie breaker
  956. ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0;
  957. else if (ctfgame.total2 > ctfgame.total1)
  958. ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
  959. else { // tie game!
  960. ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0;
  961. ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
  962. }
  963. }
  964. // tech icon
  965. i = 0;
  966. ent->client->ps.stats[STAT_CTF_TECH] = 0;
  967. while (tnames[i]) {
  968. if ((tech = FindItemByClassname(tnames[i])) != NULL &&
  969. ent->client->pers.inventory[ITEM_INDEX(tech)]) {
  970. ent->client->ps.stats[STAT_CTF_TECH] = gi.imageindex(tech->icon);
  971. break;
  972. }
  973. i++;
  974. }
  975. // figure out what icon to display for team logos
  976. // three states:
  977. // flag at base
  978. // flag taken
  979. // flag dropped
  980. p1 = imageindex_i_ctf1;
  981. e = G_Find(NULL, FOFS(classname), "item_flag_team1");
  982. if (e != NULL) {
  983. if (e->solid == SOLID_NOT) {
  984. int i;
  985. // not at base
  986. // check if on player
  987. p1 = imageindex_i_ctf1d; // default to dropped
  988. for (i = 1; i <= maxclients->value; i++)
  989. if (g_edicts[i].inuse &&
  990. g_edicts[i].client->pers.inventory[ITEM_INDEX(flag1_item)]) {
  991. // enemy has it
  992. p1 = imageindex_i_ctf1t;
  993. break;
  994. }
  995. } else if (e->spawnflags & DROPPED_ITEM)
  996. p1 = imageindex_i_ctf1d; // must be dropped
  997. }
  998. p2 = imageindex_i_ctf2;
  999. e = G_Find(NULL, FOFS(classname), "item_flag_team2");
  1000. if (e != NULL) {
  1001. if (e->solid == SOLID_NOT) {
  1002. int i;
  1003. // not at base
  1004. // check if on player
  1005. p2 = imageindex_i_ctf2d; // default to dropped
  1006. for (i = 1; i <= maxclients->value; i++)
  1007. if (g_edicts[i].inuse &&
  1008. g_edicts[i].client->pers.inventory[ITEM_INDEX(flag2_item)]) {
  1009. // enemy has it
  1010. p2 = imageindex_i_ctf2t;
  1011. break;
  1012. }
  1013. } else if (e->spawnflags & DROPPED_ITEM)
  1014. p2 = imageindex_i_ctf2d; // must be dropped
  1015. }
  1016. ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = p1;
  1017. ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = p2;
  1018. if (ctfgame.last_flag_capture && level.time - ctfgame.last_flag_capture < 5) {
  1019. if (ctfgame.last_capture_team == CTF_TEAM1)
  1020. if (level.framenum & 8)
  1021. ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = p1;
  1022. else
  1023. ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = 0;
  1024. else
  1025. if (level.framenum & 8)
  1026. ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = p2;
  1027. else
  1028. ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = 0;
  1029. }
  1030. ent->client->ps.stats[STAT_CTF_TEAM1_CAPS] = ctfgame.team1;
  1031. ent->client->ps.stats[STAT_CTF_TEAM2_CAPS] = ctfgame.team2;
  1032. ent->client->ps.stats[STAT_CTF_FLAG_PIC] = 0;
  1033. if (ent->client->resp.ctf_team == CTF_TEAM1 &&
  1034. ent->client->pers.inventory[ITEM_INDEX(flag2_item)] &&
  1035. (level.framenum & 8))
  1036. ent->client->ps.stats[STAT_CTF_FLAG_PIC] = imageindex_i_ctf2;
  1037. else if (ent->client->resp.ctf_team == CTF_TEAM2 &&
  1038. ent->client->pers.inventory[ITEM_INDEX(flag1_item)] &&
  1039. (level.framenum & 8))
  1040. ent->client->ps.stats[STAT_CTF_FLAG_PIC] = imageindex_i_ctf1;
  1041. ent->client->ps.stats[STAT_CTF_JOINED_TEAM1_PIC] = 0;
  1042. ent->client->ps.stats[STAT_CTF_JOINED_TEAM2_PIC] = 0;
  1043. if (ent->client->resp.ctf_team == CTF_TEAM1)
  1044. ent->client->ps.stats[STAT_CTF_JOINED_TEAM1_PIC] = imageindex_i_ctfj;
  1045. else if (ent->client->resp.ctf_team == CTF_TEAM2)
  1046. ent->client->ps.stats[STAT_CTF_JOINED_TEAM2_PIC] = imageindex_i_ctfj;
  1047. if (ent->client->resp.id_state)
  1048. CTFSetIDView(ent);
  1049. else {
  1050. ent->client->ps.stats[STAT_CTF_ID_VIEW] = 0;
  1051. ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = 0;
  1052. }
  1053. }
  1054. /*------------------------------------------------------------------------*/
  1055. /*QUAKED info_player_team1 (1 0 0) (-16 -16 -24) (16 16 32)
  1056. potential team1 spawning position for ctf games
  1057. */
  1058. void SP_info_player_team1(edict_t *self)
  1059. {
  1060. }
  1061. /*QUAKED info_player_team2 (0 0 1) (-16 -16 -24) (16 16 32)
  1062. potential team2 spawning position for ctf games
  1063. */
  1064. void SP_info_player_team2(edict_t *self)
  1065. {
  1066. }
  1067. /*------------------------------------------------------------------------*/
  1068. /* GRAPPLE */
  1069. /*------------------------------------------------------------------------*/
  1070. // ent is player
  1071. void CTFPlayerResetGrapple(edict_t *ent)
  1072. {
  1073. if (ent->client && ent->client->ctf_grapple)
  1074. CTFResetGrapple(ent->client->ctf_grapple);
  1075. }
  1076. // self is grapple, not player
  1077. void CTFResetGrapple(edict_t *self)
  1078. {
  1079. if (self->owner->client->ctf_grapple) {
  1080. float volume = 1.0;
  1081. gclient_t *cl;
  1082. if (self->owner->client->silencer_shots)
  1083. volume = 0.2;
  1084. gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grreset.wav"), volume, ATTN_NORM, 0);
  1085. cl = self->owner->client;
  1086. cl->ctf_grapple = NULL;
  1087. cl->ctf_grapplereleasetime = level.time;
  1088. cl->ctf_grapplestate = CTF_GRAPPLE_STATE_FLY; // we're firing, not on hook
  1089. cl->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
  1090. G_FreeEdict(self);
  1091. }
  1092. }
  1093. void CTFGrappleTouch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  1094. {
  1095. float volume = 1.0;
  1096. if (other == self->owner)
  1097. return;
  1098. if (self->owner->client->ctf_grapplestate != CTF_GRAPPLE_STATE_FLY)
  1099. return;
  1100. if (surf && (surf->flags & SURF_SKY))
  1101. {
  1102. CTFResetGrapple(self);
  1103. return;
  1104. }
  1105. VectorCopy(vec3_origin, self->velocity);
  1106. PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
  1107. if (other->takedamage) {
  1108. T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, 0, MOD_GRAPPLE);
  1109. CTFResetGrapple(self);
  1110. return;
  1111. }
  1112. self->owner->client->ctf_grapplestate = CTF_GRAPPLE_STATE_PULL; // we're on hook
  1113. self->enemy = other;
  1114. self->solid = SOLID_NOT;
  1115. if (self->owner->client->silencer_shots)
  1116. volume = 0.2;
  1117. gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grpull.wav"), volume, ATTN_NORM, 0);
  1118. gi.sound (self, CHAN_WEAPON, gi.soundindex("weapons/grapple/grhit.wav"), volume, ATTN_NORM, 0);
  1119. gi.WriteByte (svc_temp_entity);
  1120. gi.WriteByte (TE_SPARKS);
  1121. gi.WritePosition (self->s.origin);
  1122. if (!plane)
  1123. gi.WriteDir (vec3_origin);
  1124. else
  1125. gi.WriteDir (plane->normal);
  1126. gi.multicast (self->s.origin, MULTICAST_PVS);
  1127. }
  1128. // draw beam between grapple and self
  1129. void CTFGrappleDrawCable(edict_t *self)
  1130. {
  1131. vec3_t offset, start, end, f, r;
  1132. vec3_t dir;
  1133. float distance;
  1134. AngleVectors (self->owner->client->v_angle, f, r, NULL);
  1135. VectorSet(offset, 16, 16, self->owner->viewheight-8);
  1136. P_ProjectSource (self->owner->client, self->owner->s.origin, offset, f, r, start);
  1137. VectorSubtract(start, self->owner->s.origin, offset);
  1138. VectorSubtract (start, self->s.origin, dir);
  1139. distance = VectorLength(dir);
  1140. // don't draw cable if close
  1141. if (distance < 64)
  1142. return;
  1143. #if 0
  1144. if (distance > 256)
  1145. return;
  1146. // check for min/max pitch
  1147. vectoangles (dir, angles);
  1148. if (angles[0] < -180)
  1149. angles[0] += 360;
  1150. if (fabs(angles[0]) > 45)
  1151. return;
  1152. trace_t tr; //!!
  1153. tr = gi.trace (start, NULL, NULL, self->s.origin, self, MASK_SHOT);
  1154. if (tr.ent != self) {
  1155. CTFResetGrapple(self);
  1156. return;
  1157. }
  1158. #endif
  1159. // adjust start for beam origin being in middle of a segment
  1160. // VectorMA (start, 8, f, start);
  1161. VectorCopy (self->s.origin, end);
  1162. // adjust end z for end spot since the monster is currently dead
  1163. // end[2] = self->absmin[2] + self->size[2] / 2;
  1164. gi.WriteByte (svc_temp_entity);
  1165. #if 1 //def USE_GRAPPLE_CABLE
  1166. gi.WriteByte (TE_GRAPPLE_CABLE);
  1167. gi.WriteShort (self->owner - g_edicts);
  1168. gi.WritePosition (self->owner->s.origin);
  1169. gi.WritePosition (end);
  1170. gi.WritePosition (offset);
  1171. #else
  1172. gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
  1173. gi.WriteShort (self - g_edicts);
  1174. gi.WritePosition (end);
  1175. gi.WritePosition (start);
  1176. #endif
  1177. gi.multicast (self->s.origin, MULTICAST_PVS);
  1178. }
  1179. void SV_AddGravity (edict_t *ent);
  1180. // pull the player toward the grapple
  1181. void CTFGrapplePull(edict_t *self)
  1182. {
  1183. vec3_t hookdir, v;
  1184. float vlen;
  1185. if (strcmp(self->owner->client->pers.weapon->classname, "weapon_grapple") == 0 &&
  1186. !self->owner->client->newweapon &&
  1187. self->owner->client->weaponstate != WEAPON_FIRING &&
  1188. self->owner->client->weaponstate != WEAPON_ACTIVATING) {
  1189. CTFResetGrapple(self);
  1190. return;
  1191. }
  1192. if (self->enemy) {
  1193. if (self->enemy->solid == SOLID_NOT) {
  1194. CTFResetGrapple(self);
  1195. return;
  1196. }
  1197. if (self->enemy->solid == SOLID_BBOX) {
  1198. VectorScale(self->enemy->size, 0.5, v);
  1199. VectorAdd(v, self->enemy->s.origin, v);
  1200. VectorAdd(v, self->enemy->mins, self->s.origin);
  1201. gi.linkentity (self);
  1202. } else
  1203. VectorCopy(self->enemy->velocity, self->velocity);
  1204. if (self->enemy->takedamage &&
  1205. !CheckTeamDamage (self->enemy, self->owner)) {
  1206. float volume = 1.0;
  1207. if (self->owner->client->silencer_shots)
  1208. volume = 0.2;
  1209. T_Damage (self->enemy, self, self->owner, self->velocity, self->s.origin, vec3_origin, 1, 1, 0, MOD_GRAPPLE);
  1210. gi.sound (self, CHAN_WEAPON, gi.soundindex("weapons/grapple/grhurt.wav"), volume, ATTN_NORM, 0);
  1211. }
  1212. if (self->enemy->deadflag) { // he died
  1213. CTFResetGrapple(self);
  1214. return;
  1215. }
  1216. }
  1217. CTFGrappleDrawCable(self);
  1218. if (self->owner->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY) {
  1219. // pull player toward grapple
  1220. // this causes icky stuff with prediction, we need to extend
  1221. // the prediction layer to include two new fields in the player
  1222. // move stuff: a point and a velocity. The client should add
  1223. // that velociy in the direction of the point
  1224. vec3_t forward, up;
  1225. AngleVectors (self->owner->client->v_angle, forward, NULL, up);
  1226. VectorCopy(self->owner->s.origin, v);
  1227. v[2] += self->owner->viewheight;
  1228. VectorSubtract (self->s.origin, v, hookdir);
  1229. vlen = VectorLength(hookdir);
  1230. if (self->owner->client->ctf_grapplestate == CTF_GRAPPLE_STATE_PULL &&
  1231. vlen < 64) {
  1232. float volume = 1.0;
  1233. if (self->owner->client->silencer_shots)
  1234. volume = 0.2;
  1235. self->owner->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
  1236. gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grhang.wav"), volume, ATTN_NORM, 0);
  1237. self->owner->client->ctf_grapplestate = CTF_GRAPPLE_STATE_HANG;
  1238. }
  1239. VectorNormalize (hookdir);
  1240. VectorScale(hookdir, CTF_GRAPPLE_PULL_SPEED, hookdir);
  1241. VectorCopy(hookdir, self->owner->velocity);
  1242. SV_AddGravity(self->owner);
  1243. }
  1244. }
  1245. void CTFFireGrapple (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect)
  1246. {
  1247. edict_t *grapple;
  1248. trace_t tr;
  1249. VectorNormalize (dir);
  1250. grapple = G_Spawn();
  1251. VectorCopy (start, grapple->s.origin);
  1252. VectorCopy (start, grapple->s.old_origin);
  1253. vectoangles (dir, grapple->s.angles);
  1254. VectorScale (dir, speed, grapple->velocity);
  1255. grapple->movetype = MOVETYPE_FLYMISSILE;
  1256. grapple->clipmask = MASK_SHOT;
  1257. grapple->solid = SOLID_BBOX;
  1258. grapple->s.effects |= effect;
  1259. VectorClear (grapple->mins);
  1260. VectorClear (grapple->maxs);
  1261. grapple->s.modelindex = gi.modelindex ("models/weapons/grapple/hook/tris.md2");
  1262. // grapple->s.sound = gi.soundindex ("misc/lasfly.wav");
  1263. grapple->owner = self;
  1264. grapple->touch = CTFGrappleTouch;
  1265. // grapple->nextthink = level.time + FRAMETIME;
  1266. // grapple->think = CTFGrappleThink;
  1267. grapple->dmg = damage;
  1268. self->client->ctf_grapple = grapple;
  1269. self->client->ctf_grapplestate = CTF_GRAPPLE_STATE_FLY; // we're firing, not on hook
  1270. gi.linkentity (grapple);
  1271. tr = gi.trace (self->s.origin, NULL, NULL, grapple->s.origin, grapple, MASK_SHOT);
  1272. if (tr.fraction < 1.0)
  1273. {
  1274. VectorMA (grapple->s.origin, -10, dir, grapple->s.origin);
  1275. grapple->touch (grapple, tr.ent, NULL, NULL);
  1276. }
  1277. }
  1278. void CTFGrappleFire (edict_t *ent, vec3_t g_offset, int damage, int effect)
  1279. {
  1280. vec3_t forward, right;
  1281. vec3_t start;
  1282. vec3_t offset;
  1283. float volume = 1.0;
  1284. if (ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY)
  1285. return; // it's already out
  1286. AngleVectors (ent->client->v_angle, forward, right, NULL);
  1287. // VectorSet(offset, 24, 16, ent->viewheight-8+2);
  1288. VectorSet(offset, 24, 8, ent->viewheight-8+2);
  1289. VectorAdd (offset, g_offset, offset);
  1290. P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
  1291. VectorScale (forward, -2, ent->client->kick_origin);
  1292. ent->client->kick_angles[0] = -1;
  1293. if (ent->client->silencer_shots)
  1294. volume = 0.2;
  1295. gi.sound (ent, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grfire.wav"), volume, ATTN_NORM, 0);
  1296. CTFFireGrapple (ent, start, forward, damage, CTF_GRAPPLE_SPEED, effect);
  1297. #if 0
  1298. // send muzzle flash
  1299. gi.WriteByte (svc_muzzleflash);
  1300. gi.WriteShort (ent-g_edicts);
  1301. gi.WriteByte (MZ_BLASTER);
  1302. gi.multicast (ent->s.origin, MULTICAST_PVS);
  1303. #endif
  1304. PlayerNoise(ent, start, PNOISE_WEAPON);
  1305. }
  1306. void CTFWeapon_Grapple_Fire (edict_t *ent)
  1307. {
  1308. int damage;
  1309. damage = 10;
  1310. CTFGrappleFire (ent, vec3_origin, damage, 0);
  1311. ent->client->ps.gunframe++;
  1312. }
  1313. void CTFWeapon_Grapple (edict_t *ent)
  1314. {
  1315. static int pause_frames[] = {10, 18, 27, 0};
  1316. static int fire_frames[] = {6, 0};
  1317. int prevstate;
  1318. // if the the attack button is still down, stay in the firing frame
  1319. if ((ent->client->buttons & BUTTON_ATTACK) &&
  1320. ent->client->weaponstate == WEAPON_FIRING &&
  1321. ent->client->ctf_grapple)
  1322. ent->client->ps.gunframe = 9;
  1323. if (!(ent->client->buttons & BUTTON_ATTACK) &&
  1324. ent->client->ctf_grapple) {
  1325. CTFResetGrapple(ent->client->ctf_grapple);
  1326. if (ent->client->weaponstate == WEAPON_FIRING)
  1327. ent->client->weaponstate = WEAPON_READY;
  1328. }
  1329. if (ent->client->newweapon &&
  1330. ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY &&
  1331. ent->client->weaponstate == WEAPON_FIRING) {
  1332. // he wants to change weapons while grappled
  1333. ent->client->weaponstate = WEAPON_DROPPING;
  1334. ent->client->ps.gunframe = 32;
  1335. }
  1336. prevstate = ent->client->weaponstate;
  1337. Weapon_Generic (ent, 5, 9, 31, 36, pause_frames, fire_frames,
  1338. CTFWeapon_Grapple_Fire);
  1339. // if we just switched back to grapple, immediately go to fire frame
  1340. if (prevstate == WEAPON_ACTIVATING &&
  1341. ent->client->weaponstate == WEAPON_READY &&
  1342. ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY) {
  1343. if (!(ent->client->buttons & BUTTON_ATTACK))
  1344. ent->client->ps.gunframe = 9;
  1345. else
  1346. ent->client->ps.gunframe = 5;
  1347. ent->client->weaponstate = WEAPON_FIRING;
  1348. }
  1349. }
  1350. void CTFTeam_f (edict_t *ent)
  1351. {
  1352. char *t, *s;
  1353. int desired_team;
  1354. t = gi.args();
  1355. if (!*t) {
  1356. gi.cprintf(ent, PRINT_HIGH, "You are on the %s team.\n",
  1357. CTFTeamName(ent->client->resp.ctf_team));
  1358. return;
  1359. }
  1360. if (ctfgame.match > MATCH_SETUP) {
  1361. gi.cprintf(ent, PRINT_HIGH, "Can't change teams in a match.\n");
  1362. return;
  1363. }
  1364. if (Q_stricmp(t, "red") == 0)
  1365. desired_team = CTF_TEAM1;
  1366. else if (Q_stricmp(t, "blue") == 0)
  1367. desired_team = CTF_TEAM2;
  1368. else {
  1369. gi.cprintf(ent, PRINT_HIGH, "Unknown team %s.\n", t);
  1370. return;
  1371. }
  1372. if (ent->client->resp.ctf_team == desired_team) {
  1373. gi.cprintf(ent, PRINT_HIGH, "You are already on the %s team.\n",
  1374. CTFTeamName(ent->client->resp.ctf_team));
  1375. return;
  1376. }
  1377. ////
  1378. ent->svflags = 0;
  1379. ent->flags &= ~FL_GODMODE;
  1380. ent->client->resp.ctf_team = desired_team;
  1381. ent->client->resp.ctf_state = 0;
  1382. s = Info_ValueForKey (ent->client->pers.userinfo, "skin");
  1383. CTFAssignSkin(ent, s);
  1384. if (ent->solid == SOLID_NOT) { // spectator
  1385. PutClientInServer (ent);
  1386. // add a teleportation effect
  1387. ent->s.event = EV_PLAYER_TELEPORT;
  1388. // hold in place briefly
  1389. ent->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
  1390. ent->client->ps.pmove.pm_time = 14;
  1391. gi.bprintf(PRINT_HIGH, "%s joined the %s team.\n",
  1392. ent->client->pers.netname, CTFTeamName(desired_team));
  1393. return;
  1394. }
  1395. ent->health = 0;
  1396. player_die (ent, ent, ent, 100000, vec3_origin);
  1397. // don't even bother waiting for death frames
  1398. ent->deadflag = DEAD_DEAD;
  1399. respawn (ent);
  1400. ent->client->resp.score = 0;
  1401. gi.bprintf(PRINT_HIGH, "%s changed to the %s team.\n",
  1402. ent->client->pers.netname, CTFTeamName(desired_team));
  1403. }
  1404. /*
  1405. ==================
  1406. CTFScoreboardMessage
  1407. ==================
  1408. */
  1409. void CTFScoreboardMessage (edict_t *ent, edict_t *killer)
  1410. {
  1411. char entry[1024];
  1412. char string[1400];
  1413. int len;
  1414. int i, j, k, n;
  1415. int sorted[2][MAX_CLIENTS];
  1416. int sortedscores[2][MAX_CLIENTS];
  1417. int score, total[2], totalscore[2];
  1418. int last[2];
  1419. gclient_t *cl;
  1420. edict_t *cl_ent;
  1421. int team;
  1422. int maxsize = 1000;
  1423. // sort the clients by team and score
  1424. total[0] = total[1] = 0;
  1425. last[0] = last[1] = 0;
  1426. totalscore[0] = totalscore[1] = 0;
  1427. for (i=0 ; i<game.maxclients ; i++)
  1428. {
  1429. cl_ent = g_edicts + 1 + i;
  1430. if (!cl_ent->inuse)
  1431. continue;
  1432. if (game.clients[i].resp.ctf_team == CTF_TEAM1)
  1433. team = 0;
  1434. else if (game.clients[i].resp.ctf_team == CTF_TEAM2)
  1435. team = 1;
  1436. else
  1437. continue; // unknown team?
  1438. score = game.clients[i].resp.score;
  1439. for (j=0 ; j<total[team] ; j++)
  1440. {
  1441. if (score > sortedscores[team][j])
  1442. break;
  1443. }
  1444. for (k=total[team] ; k>j ; k--)
  1445. {
  1446. sorted[team][k] = sorted[team][k-1];
  1447. sortedscores[team][k] = sortedscores[team][k-1];
  1448. }
  1449. sorted[team][j] = i;
  1450. sortedscores[team][j] = score;
  1451. totalscore[team] += score;
  1452. total[team]++;
  1453. }
  1454. // print level name and exit rules
  1455. // add the clients in sorted order
  1456. *string = 0;
  1457. len = 0;
  1458. // team one
  1459. sprintf(string, "if 24 xv 8 yv 8 pic 24 endif "
  1460. "xv 40 yv 28 string \"%4d/%-3d\" "
  1461. "xv 98 yv 12 num 2 18 "
  1462. "if 25 xv 168 yv 8 pic 25 endif "
  1463. "xv 200 yv 28 string \"%4d/%-3d\" "
  1464. "xv 256 yv 12 num 2 20 ",
  1465. totalscore[0], total[0],
  1466. totalscore[1], total[1]);
  1467. len = strlen(string);
  1468. for (i=0 ; i<16 ; i++)
  1469. {
  1470. if (i >= total[0] && i >= total[1])
  1471. break; // we're done
  1472. #if 0 //ndef NEW_SCORE
  1473. // set up y
  1474. sprintf(entry, "yv %d ", 42 + i * 8);
  1475. if (maxsize - len > strlen(entry)) {
  1476. strcat(string, entry);
  1477. len = strlen(string);
  1478. }
  1479. #else
  1480. *entry = 0;
  1481. #endif
  1482. // left side
  1483. if (i < total[0]) {
  1484. cl = &game.clients[sorted[0][i]];
  1485. cl_ent = g_edicts + 1 + sorted[0][i];
  1486. #if 0 //ndef NEW_SCORE
  1487. sprintf(entry+strlen(entry),
  1488. "xv 0 %s \"%3d %3d %-12.12s\" ",
  1489. (cl_ent == ent) ? "string2" : "string",
  1490. cl->resp.score,
  1491. (cl->ping > 999) ? 999 : cl->ping,
  1492. cl->pers.netname);
  1493. if (cl_ent->client->pers.inventory[ITEM_INDEX(flag2_item)])
  1494. strcat(entry, "xv 56 picn sbfctf2 ");
  1495. #else
  1496. sprintf(entry+strlen(entry),
  1497. "ctf 0 %d %d %d %d ",
  1498. 42 + i * 8,
  1499. sorted[0][i],
  1500. cl->resp.score,
  1501. cl->ping > 999 ? 999 : cl->ping);
  1502. if (cl_ent->client->pers.inventory[ITEM_INDEX(flag2_item)])
  1503. sprintf(entry + strlen(entry), "xv 56 yv %d picn sbfctf2 ",
  1504. 42 + i * 8);
  1505. #endif
  1506. if (maxsize - len > strlen(entry)) {
  1507. strcat(string, entry);
  1508. len = strlen(string);
  1509. last[0] = i;
  1510. }
  1511. }
  1512. // right side
  1513. if (i < total[1]) {
  1514. cl = &game.clients[sorted[1][i]];
  1515. cl_ent = g_edicts + 1 + sorted[1][i];
  1516. #if 0 //ndef NEW_SCORE
  1517. sprintf(entry+strlen(entry),
  1518. "xv 160 %s \"%3d %3d %-12.12s\" ",
  1519. (cl_ent == ent) ? "string2" : "string",
  1520. cl->resp.score,
  1521. (cl->ping > 999) ? 999 : cl->ping,
  1522. cl->pers.netname);
  1523. if (cl_ent->client->pers.inventory[ITEM_INDEX(flag1_item)])
  1524. strcat(entry, "xv 216 picn sbfctf1 ");
  1525. #else
  1526. sprintf(entry+strlen(entry),
  1527. "ctf 160 %d %d %d %d ",
  1528. 42 + i * 8,
  1529. sorted[1][i],
  1530. cl->resp.score,
  1531. cl->ping > 999 ? 999 : cl->ping);
  1532. if (cl_ent->client->pers.inventory[ITEM_INDEX(flag1_item)])
  1533. sprintf(entry + strlen(entry), "xv 216 yv %d picn sbfctf1 ",
  1534. 42 + i * 8);
  1535. #endif
  1536. if (maxsize - len > strlen(entry)) {
  1537. strcat(string, entry);
  1538. len = strlen(string);
  1539. last[1] = i;
  1540. }
  1541. }
  1542. }
  1543. // put in spectators if we have enough room
  1544. if (last[0] > last[1])
  1545. j = last[0];
  1546. else
  1547. j = last[1];
  1548. j = (j + 2) * 8 + 42;
  1549. k = n = 0;
  1550. if (maxsize - len > 50) {
  1551. for (i = 0; i < maxclients->value; i++) {
  1552. cl_ent = g_edicts + 1 + i;
  1553. cl = &game.clients[i];
  1554. if (!cl_ent->inuse ||
  1555. cl_ent->solid != SOLID_NOT ||
  1556. cl_ent->client->resp.ctf_team != CTF_NOTEAM)
  1557. continue;
  1558. if (!k) {
  1559. k = 1;
  1560. sprintf(entry, "xv 0 yv %d string2 \"Spectators\" ", j);
  1561. strcat(string, entry);
  1562. len = strlen(string);
  1563. j += 8;
  1564. }
  1565. sprintf(entry+strlen(entry),
  1566. "ctf %d %d %d %d %d ",
  1567. (n & 1) ? 160 : 0, // x
  1568. j, // y
  1569. i, // playernum
  1570. cl->resp.score,
  1571. cl->ping > 999 ? 999 : cl->ping);
  1572. if (maxsize - len > strlen(entry)) {
  1573. strcat(string, entry);
  1574. len = strlen(string);
  1575. }
  1576. if (n & 1)
  1577. j += 8;
  1578. n++;
  1579. }
  1580. }
  1581. if (total[0] - last[0] > 1) // couldn't fit everyone
  1582. sprintf(string + strlen(string), "xv 8 yv %d string \"..and %d more\" ",
  1583. 42 + (last[0]+1)*8, total[0] - last[0] - 1);
  1584. if (total[1] - last[1] > 1) // couldn't fit everyone
  1585. sprintf(string + strlen(string), "xv 168 yv %d string \"..and %d more\" ",
  1586. 42 + (last[1]+1)*8, total[1] - last[1] - 1);
  1587. gi.WriteByte (svc_layout);
  1588. gi.WriteString (string);
  1589. }
  1590. /*------------------------------------------------------------------------*/
  1591. /* TECH */
  1592. /*------------------------------------------------------------------------*/
  1593. void CTFHasTech(edict_t *who)
  1594. {
  1595. if (level.time - who->client->ctf_lasttechmsg > 2) {
  1596. gi.centerprintf(who, "You already have a TECH powerup.");
  1597. who->client->ctf_lasttechmsg = level.time;
  1598. }
  1599. }
  1600. gitem_t *CTFWhat_Tech(edict_t *ent)
  1601. {
  1602. gitem_t *tech;
  1603. int i;
  1604. i = 0;
  1605. while (tnames[i]) {
  1606. if ((tech = FindItemByClassname(tnames[i])) != NULL &&
  1607. ent->client->pers.inventory[ITEM_INDEX(tech)]) {
  1608. return tech;
  1609. }
  1610. i++;
  1611. }
  1612. return NULL;
  1613. }
  1614. qboolean CTFPickup_Tech (edict_t *ent, edict_t *other)
  1615. {
  1616. gitem_t *tech;
  1617. int i;
  1618. i = 0;
  1619. while (tnames[i]) {
  1620. if ((tech = FindItemByClassname(tnames[i])) != NULL &&
  1621. other->client->pers.inventory[ITEM_INDEX(tech)]) {
  1622. CTFHasTech(other);
  1623. return false; // has this one
  1624. }
  1625. i++;
  1626. }
  1627. // client only gets one tech
  1628. other->client->pers.inventory[ITEM_INDEX(ent->item)]++;
  1629. other->client->ctf_regentime = level.time;
  1630. return true;
  1631. }
  1632. static void SpawnTech(gitem_t *item, edict_t *spot);
  1633. static edict_t *FindTechSpawn(void)
  1634. {
  1635. edict_t *spot = NULL;
  1636. int i = rand() % 16;
  1637. while (i--)
  1638. spot = G_Find (spot, FOFS(classname), "info_player_deathmatch");
  1639. if (!spot)
  1640. spot = G_Find (spot, FOFS(classname), "info_player_deathmatch");
  1641. return spot;
  1642. }
  1643. static void TechThink(edict_t *tech)
  1644. {
  1645. edict_t *spot;
  1646. if ((spot = FindTechSpawn()) != NULL) {
  1647. SpawnTech(tech->item, spot);
  1648. G_FreeEdict(tech);
  1649. } else {
  1650. tech->nextthink = level.time + CTF_TECH_TIMEOUT;
  1651. tech->think = TechThink;
  1652. }
  1653. }
  1654. void CTFDrop_Tech(edict_t *ent, gitem_t *item)
  1655. {
  1656. edict_t *tech;
  1657. tech = Drop_Item(ent, item);
  1658. tech->nextthink = level.time + CTF_TECH_TIMEOUT;
  1659. tech->think = TechThink;
  1660. ent->client->pers.inventory[ITEM_INDEX(item)] = 0;
  1661. }
  1662. void CTFDeadDropTech(edict_t *ent)
  1663. {
  1664. gitem_t *tech;
  1665. edict_t *dropped;
  1666. int i;
  1667. i = 0;
  1668. while (tnames[i]) {
  1669. if ((tech = FindItemByClassname(tnames[i])) != NULL &&
  1670. ent->client->pers.inventory[ITEM_INDEX(tech)]) {
  1671. dropped = Drop_Item(ent, tech);
  1672. // hack the velocity to make it bounce random
  1673. dropped->velocity[0] = (rand() % 600) - 300;
  1674. dropped->velocity[1] = (rand() % 600) - 300;
  1675. dropped->nextthink = level.time + CTF_TECH_TIMEOUT;
  1676. dropped->think = TechThink;
  1677. dropped->owner = NULL;
  1678. ent->client->pers.inventory[ITEM_INDEX(tech)] = 0;
  1679. }
  1680. i++;
  1681. }
  1682. }
  1683. static void SpawnTech(gitem_t *item, edict_t *spot)
  1684. {
  1685. edict_t *ent;
  1686. vec3_t forward, right;
  1687. vec3_t angles;
  1688. ent = G_Spawn();
  1689. ent->classname = item->classname;
  1690. ent->item = item;
  1691. ent->spawnflags = DROPPED_ITEM;
  1692. ent->s.effects = item->world_model_flags;
  1693. ent->s.renderfx = RF_GLOW;
  1694. VectorSet (ent->mins, -15, -15, -15);
  1695. VectorSet (ent->maxs, 15, 15, 15);
  1696. gi.setmodel (ent, ent->item->world_model);
  1697. ent->solid = SOLID_TRIGGER;
  1698. ent->movetype = MOVETYPE_TOSS;
  1699. ent->touch = Touch_Item;
  1700. ent->owner = ent;
  1701. angles[0] = 0;
  1702. angles[1] = rand() % 360;
  1703. angles[2] = 0;
  1704. AngleVectors (angles, forward, right, NULL);
  1705. VectorCopy (spot->s.origin, ent->s.origin);
  1706. ent->s.origin[2] += 16;
  1707. VectorScale (forward, 100, ent->velocity);
  1708. ent->velocity[2] = 300;
  1709. ent->nextthink = level.time + CTF_TECH_TIMEOUT;
  1710. ent->think = TechThink;
  1711. gi.linkentity (ent);
  1712. }
  1713. static void SpawnTechs(edict_t *ent)
  1714. {
  1715. gitem_t *tech;
  1716. edict_t *spot;
  1717. int i;
  1718. i = 0;
  1719. while (tnames[i]) {
  1720. if ((tech = FindItemByClassname(tnames[i])) != NULL &&
  1721. (spot = FindTechSpawn()) != NULL)
  1722. SpawnTech(tech, spot);
  1723. i++;
  1724. }
  1725. if (ent)
  1726. G_FreeEdict(ent);
  1727. }
  1728. // frees the passed edict!
  1729. void CTFRespawnTech(edict_t *ent)
  1730. {
  1731. edict_t *spot;
  1732. if ((spot = FindTechSpawn()) != NULL)
  1733. SpawnTech(ent->item, spot);
  1734. G_FreeEdict(ent);
  1735. }
  1736. void CTFSetupTechSpawn(void)
  1737. {
  1738. edict_t *ent;
  1739. if (((int)dmflags->value & DF_CTF_NO_TECH))
  1740. return;
  1741. ent = G_Spawn();
  1742. ent->nextthink = level.time + 2;
  1743. ent->think = SpawnTechs;
  1744. }
  1745. void CTFResetTech(void)
  1746. {
  1747. edict_t *ent;
  1748. int i;
  1749. for (ent = g_edicts + 1, i = 1; i < globals.num_edicts; i++, ent++) {
  1750. if (ent->inuse)
  1751. if (ent->item && (ent->item->flags & IT_TECH))
  1752. G_FreeEdict(ent);
  1753. }
  1754. SpawnTechs(NULL);
  1755. }
  1756. int CTFApplyResistance(edict_t *ent, int dmg)
  1757. {
  1758. static gitem_t *tech = NULL;
  1759. float volume = 1.0;
  1760. if (ent->client && ent->client->silencer_shots)
  1761. volume = 0.2;
  1762. if (!tech)
  1763. tech = FindItemByClassname("item_tech1");
  1764. if (dmg && tech && ent->client && ent->client->pers.inventory[ITEM_INDEX(tech)]) {
  1765. // make noise
  1766. gi.sound(ent, CHAN_VOICE, gi.soundindex("ctf/tech1.wav"), volume, ATTN_NORM, 0);
  1767. return dmg / 2;
  1768. }
  1769. return dmg;
  1770. }
  1771. int CTFApplyStrength(edict_t *ent, int dmg)
  1772. {
  1773. static gitem_t *tech = NULL;
  1774. if (!tech)
  1775. tech = FindItemByClassname("item_tech2");
  1776. if (dmg && tech && ent->client && ent->client->pers.inventory[ITEM_INDEX(tech)]) {
  1777. return dmg * 2;
  1778. }
  1779. return dmg;
  1780. }
  1781. qboolean CTFApplyStrengthSound(edict_t *ent)
  1782. {
  1783. static gitem_t *tech = NULL;
  1784. float volume = 1.0;
  1785. if (ent->client && ent->client->silencer_shots)
  1786. volume = 0.2;
  1787. if (!tech)
  1788. tech = FindItemByClassname("item_tech2");
  1789. if (tech && ent->client &&
  1790. ent->client->pers.inventory[ITEM_INDEX(tech)]) {
  1791. if (ent->client->ctf_techsndtime < level.time) {
  1792. ent->client->ctf_techsndtime = level.time + 1;
  1793. if (ent->client->quad_framenum > level.framenum)
  1794. gi.sound(ent, CHAN_VOICE, gi.soundindex("ctf/tech2x.wav"), volume, ATTN_NORM, 0);
  1795. else
  1796. gi.sound(ent, CHAN_VOICE, gi.soundindex("ctf/tech2.wav"), volume, ATTN_NORM, 0);
  1797. }
  1798. return true;
  1799. }
  1800. return false;
  1801. }
  1802. qboolean CTFApplyHaste(edict_t *ent)
  1803. {
  1804. static gitem_t *tech = NULL;
  1805. if (!tech)
  1806. tech = FindItemByClassname("item_tech3");
  1807. if (tech && ent->client &&
  1808. ent->client->pers.inventory[ITEM_INDEX(tech)])
  1809. return true;
  1810. return false;
  1811. }
  1812. void CTFApplyHasteSound(edict_t *ent)
  1813. {
  1814. static gitem_t *tech = NULL;
  1815. float volume = 1.0;
  1816. if (ent->client && ent->client->silencer_shots)
  1817. volume = 0.2;
  1818. if (!tech)
  1819. tech = FindItemByClassname("item_tech3");
  1820. if (tech && ent->client &&
  1821. ent->client->pers.inventory[ITEM_INDEX(tech)] &&
  1822. ent->client->ctf_techsndtime < level.time) {
  1823. ent->client->ctf_techsndtime = level.time + 1;
  1824. gi.sound(ent, CHAN_VOICE, gi.soundindex("ctf/tech3.wav"), volume, ATTN_NORM, 0);
  1825. }
  1826. }
  1827. void CTFApplyRegeneration(edict_t *ent)
  1828. {
  1829. static gitem_t *tech = NULL;
  1830. qboolean noise = false;
  1831. gclient_t *client;
  1832. int index;
  1833. float volume = 1.0;
  1834. client = ent->client;
  1835. if (!client)
  1836. return;
  1837. if (ent->client->silencer_shots)
  1838. volume = 0.2;
  1839. if (!tech)
  1840. tech = FindItemByClassname("item_tech4");
  1841. if (tech && client->pers.inventory[ITEM_INDEX(tech)]) {
  1842. if (client->ctf_regentime < level.time) {
  1843. client->ctf_regentime = level.time;
  1844. if (ent->health < 150) {
  1845. ent->health += 5;
  1846. if (ent->health > 150)
  1847. ent->health = 150;
  1848. client->ctf_regentime += 0.5;
  1849. noise = true;
  1850. }
  1851. index = ArmorIndex (ent);
  1852. if (index && client->pers.inventory[index] < 150) {
  1853. client->pers.inventory[index] += 5;
  1854. if (client->pers.inventory[index] > 150)
  1855. client->pers.inventory[index] = 150;
  1856. client->ctf_regentime += 0.5;
  1857. noise = true;
  1858. }
  1859. }
  1860. if (noise && ent->client->ctf_techsndtime < level.time) {
  1861. ent->client->ctf_techsndtime = level.time + 1;
  1862. gi.sound(ent, CHAN_VOICE, gi.soundindex("ctf/tech4.wav"), volume, ATTN_NORM, 0);
  1863. }
  1864. }
  1865. }
  1866. qboolean CTFHasRegeneration(edict_t *ent)
  1867. {
  1868. static gitem_t *tech = NULL;
  1869. if (!tech)
  1870. tech = FindItemByClassname("item_tech4");
  1871. if (tech && ent->client &&
  1872. ent->client->pers.inventory[ITEM_INDEX(tech)])
  1873. return true;
  1874. return false;
  1875. }
  1876. /*
  1877. ======================================================================
  1878. SAY_TEAM
  1879. ======================================================================
  1880. */
  1881. // This array is in 'importance order', it indicates what items are
  1882. // more important when reporting their names.
  1883. struct {
  1884. char *classname;
  1885. int priority;
  1886. } loc_names[] =
  1887. {
  1888. { "item_flag_team1", 1 },
  1889. { "item_flag_team2", 1 },
  1890. { "item_quad", 2 },
  1891. { "item_invulnerability", 2 },
  1892. { "weapon_bfg", 3 },
  1893. { "weapon_railgun", 4 },
  1894. { "weapon_rocketlauncher", 4 },
  1895. { "weapon_hyperblaster", 4 },
  1896. { "weapon_chaingun", 4 },
  1897. { "weapon_grenadelauncher", 4 },
  1898. { "weapon_machinegun", 4 },
  1899. { "weapon_supershotgun", 4 },
  1900. { "weapon_shotgun", 4 },
  1901. { "item_power_screen", 5 },
  1902. { "item_power_shield", 5 },
  1903. { "item_armor_body", 6 },
  1904. { "item_armor_combat", 6 },
  1905. { "item_armor_jacket", 6 },
  1906. { "item_silencer", 7 },
  1907. { "item_breather", 7 },
  1908. { "item_enviro", 7 },
  1909. { "item_adrenaline", 7 },
  1910. { "item_bandolier", 8 },
  1911. { "item_pack", 8 },
  1912. { NULL, 0 }
  1913. };
  1914. static void CTFSay_Team_Location(edict_t *who, char *buf)
  1915. {
  1916. edict_t *what = NULL;
  1917. edict_t *hot = NULL;
  1918. float hotdist = 999999, newdist;
  1919. vec3_t v;
  1920. int hotindex = 999;
  1921. int i;
  1922. gitem_t *item;
  1923. int nearteam = -1;
  1924. edict_t *flag1, *flag2;
  1925. qboolean hotsee = false;
  1926. qboolean cansee;
  1927. while ((what = loc_findradius(what, who->s.origin, 1024)) != NULL) {
  1928. // find what in loc_classnames
  1929. for (i = 0; loc_names[i].classname; i++)
  1930. if (strcmp(what->classname, loc_names[i].classname) == 0)
  1931. break;
  1932. if (!loc_names[i].classname)
  1933. continue;
  1934. // something we can see get priority over something we can't
  1935. cansee = loc_CanSee(what, who);
  1936. if (cansee && !hotsee) {
  1937. hotsee = true;
  1938. hotindex = loc_names[i].priority;
  1939. hot = what;
  1940. VectorSubtract(what->s.origin, who->s.origin, v);
  1941. hotdist = VectorLength(v);
  1942. continue;
  1943. }
  1944. // if we can't see this, but we have something we can see, skip it
  1945. if (hotsee && !cansee)
  1946. continue;
  1947. if (hotsee && hotindex < loc_names[i].priority)
  1948. continue;
  1949. VectorSubtract(what->s.origin, who->s.origin, v);
  1950. newdist = VectorLength(v);
  1951. if (newdist < hotdist ||
  1952. (cansee && loc_names[i].priority < hotindex)) {
  1953. hot = what;
  1954. hotdist = newdist;
  1955. hotindex = i;
  1956. hotsee = loc_CanSee(hot, who);
  1957. }
  1958. }
  1959. if (!hot) {
  1960. strcpy(buf, "nowhere");
  1961. return;
  1962. }
  1963. // we now have the closest item
  1964. // see if there's more than one in the map, if so
  1965. // we need to determine what team is closest
  1966. what = NULL;
  1967. while ((what = G_Find(what, FOFS(classname), hot->classname)) != NULL) {
  1968. if (what == hot)
  1969. continue;
  1970. // if we are here, there is more than one, find out if hot
  1971. // is closer to red flag or blue flag
  1972. if ((flag1 = G_Find(NULL, FOFS(classname), "item_flag_team1")) != NULL &&
  1973. (flag2 = G_Find(NULL, FOFS(classname), "item_flag_team2")) != NULL) {
  1974. VectorSubtract(hot->s.origin, flag1->s.origin, v);
  1975. hotdist = VectorLength(v);
  1976. VectorSubtract(hot->s.origin, flag2->s.origin, v);
  1977. newdist = VectorLength(v);
  1978. if (hotdist < newdist)
  1979. nearteam = CTF_TEAM1;
  1980. else if (hotdist > newdist)
  1981. nearteam = CTF_TEAM2;
  1982. }
  1983. break;
  1984. }
  1985. if ((item = FindItemByClassname(hot->classname)) == NULL) {
  1986. strcpy(buf, "nowhere");
  1987. return;
  1988. }
  1989. // in water?
  1990. if (who->waterlevel)
  1991. strcpy(buf, "in the water ");
  1992. else
  1993. *buf = 0;
  1994. // near or above
  1995. VectorSubtract(who->s.origin, hot->s.origin, v);
  1996. if (fabs(v[2]) > fabs(v[0]) && fabs(v[2]) > fabs(v[1]))
  1997. if (v[2] > 0)
  1998. strcat(buf, "above ");
  1999. else
  2000. strcat(buf, "below ");
  2001. else
  2002. strcat(buf, "near ");
  2003. if (nearteam == CTF_TEAM1)
  2004. strcat(buf, "the red ");
  2005. else if (nearteam == CTF_TEAM2)
  2006. strcat(buf, "the blue ");
  2007. else
  2008. strcat(buf, "the ");
  2009. strcat(buf, item->pickup_name);
  2010. }
  2011. static void CTFSay_Team_Armor(edict_t *who, char *buf)
  2012. {
  2013. gitem_t *item;
  2014. int index, cells;
  2015. int power_armor_type;
  2016. *buf = 0;
  2017. power_armor_type = PowerArmorType (who);
  2018. if (power_armor_type)
  2019. {
  2020. cells = who->client->pers.inventory[ITEM_INDEX(FindItem ("cells"))];
  2021. if (cells)
  2022. sprintf(buf+strlen(buf), "%s with %i cells ",
  2023. (power_armor_type == POWER_ARMOR_SCREEN) ?
  2024. "Power Screen" : "Power Shield", cells);
  2025. }
  2026. index = ArmorIndex (who);
  2027. if (index)
  2028. {
  2029. item = GetItemByIndex (index);
  2030. if (item) {
  2031. if (*buf)
  2032. strcat(buf, "and ");
  2033. sprintf(buf+strlen(buf), "%i units of %s",
  2034. who->client->pers.inventory[index], item->pickup_name);
  2035. }
  2036. }
  2037. if (!*buf)
  2038. strcpy(buf, "no armor");
  2039. }
  2040. static void CTFSay_Team_Health(edict_t *who, char *buf)
  2041. {
  2042. if (who->health <= 0)
  2043. strcpy(buf, "dead");
  2044. else
  2045. sprintf(buf, "%i health", who->health);
  2046. }
  2047. static void CTFSay_Team_Tech(edict_t *who, char *buf)
  2048. {
  2049. gitem_t *tech;
  2050. int i;
  2051. // see if the player has a tech powerup
  2052. i = 0;
  2053. while (tnames[i]) {
  2054. if ((tech = FindItemByClassname(tnames[i])) != NULL &&
  2055. who->client->pers.inventory[ITEM_INDEX(tech)]) {
  2056. sprintf(buf, "the %s", tech->pickup_name);
  2057. return;
  2058. }
  2059. i++;
  2060. }
  2061. strcpy(buf, "no powerup");
  2062. }
  2063. static void CTFSay_Team_Weapon(edict_t *who, char *buf)
  2064. {
  2065. if (who->client->pers.weapon)
  2066. strcpy(buf, who->client->pers.weapon->pickup_name);
  2067. else
  2068. strcpy(buf, "none");
  2069. }
  2070. static void CTFSay_Team_Sight(edict_t *who, char *buf)
  2071. {
  2072. int i;
  2073. edict_t *targ;
  2074. int n = 0;
  2075. char s[1024];
  2076. char s2[1024];
  2077. *s = *s2 = 0;
  2078. for (i = 1; i <= maxclients->value; i++) {
  2079. targ = g_edicts + i;
  2080. if (!targ->inuse ||
  2081. targ == who ||
  2082. !loc_CanSee(targ, who))
  2083. continue;
  2084. if (*s2) {
  2085. if (strlen(s) + strlen(s2) + 3 < sizeof(s)) {
  2086. if (n)
  2087. strcat(s, ", ");
  2088. strcat(s, s2);
  2089. *s2 = 0;
  2090. }
  2091. n++;
  2092. }
  2093. strcpy(s2, targ->client->pers.netname);
  2094. }
  2095. if (*s2) {
  2096. if (strlen(s) + strlen(s2) + 6 < sizeof(s)) {
  2097. if (n)
  2098. strcat(s, " and ");
  2099. strcat(s, s2);
  2100. }
  2101. strcpy(buf, s);
  2102. } else
  2103. strcpy(buf, "no one");
  2104. }
  2105. void CTFSay_Team(edict_t *who, char *msg)
  2106. {
  2107. char outmsg[256];
  2108. char buf[256];
  2109. int i;
  2110. char *p;
  2111. edict_t *cl_ent;
  2112. if (CheckFlood(who))
  2113. return;
  2114. outmsg[0] = 0;
  2115. if (*msg == '\"') {
  2116. msg[strlen(msg) - 1] = 0;
  2117. msg++;
  2118. }
  2119. for (p = outmsg; *msg && (p - outmsg) < sizeof(outmsg) - 2; msg++) {
  2120. if (*msg == '%') {
  2121. switch (*++msg) {
  2122. case 'l' :
  2123. case 'L' :
  2124. CTFSay_Team_Location(who, buf);
  2125. if (strlen(buf) + (p - outmsg) < sizeof(outmsg) - 2) {
  2126. strcpy(p, buf);
  2127. p += strlen(buf);
  2128. }
  2129. break;
  2130. case 'a' :
  2131. case 'A' :
  2132. CTFSay_Team_Armor(who, buf);
  2133. if (strlen(buf) + (p - outmsg) < sizeof(outmsg) - 2) {
  2134. strcpy(p, buf);
  2135. p += strlen(buf);
  2136. }
  2137. break;
  2138. case 'h' :
  2139. case 'H' :
  2140. CTFSay_Team_Health(who, buf);
  2141. if (strlen(buf) + (p - outmsg) < sizeof(outmsg) - 2) {
  2142. strcpy(p, buf);
  2143. p += strlen(buf);
  2144. }
  2145. break;
  2146. case 't' :
  2147. case 'T' :
  2148. CTFSay_Team_Tech(who, buf);
  2149. if (strlen(buf) + (p - outmsg) < sizeof(outmsg) - 2) {
  2150. strcpy(p, buf);
  2151. p += strlen(buf);
  2152. }
  2153. break;
  2154. case 'w' :
  2155. case 'W' :
  2156. CTFSay_Team_Weapon(who, buf);
  2157. if (strlen(buf) + (p - outmsg) < sizeof(outmsg) - 2) {
  2158. strcpy(p, buf);
  2159. p += strlen(buf);
  2160. }
  2161. break;
  2162. case 'n' :
  2163. case 'N' :
  2164. CTFSay_Team_Sight(who, buf);
  2165. if (strlen(buf) + (p - outmsg) < sizeof(outmsg) - 2) {
  2166. strcpy(p, buf);
  2167. p += strlen(buf);
  2168. }
  2169. break;
  2170. default :
  2171. *p++ = *msg;
  2172. }
  2173. } else
  2174. *p++ = *msg;
  2175. }
  2176. *p = 0;
  2177. for (i = 0; i < maxclients->value; i++) {
  2178. cl_ent = g_edicts + 1 + i;
  2179. if (!cl_ent->inuse)
  2180. continue;
  2181. if (cl_ent->client->resp.ctf_team == who->client->resp.ctf_team)
  2182. gi.cprintf(cl_ent, PRINT_CHAT, "(%s): %s\n",
  2183. who->client->pers.netname, outmsg);
  2184. }
  2185. }
  2186. /*-----------------------------------------------------------------------*/
  2187. /*QUAKED misc_ctf_banner (1 .5 0) (-4 -64 0) (4 64 248) TEAM2
  2188. The origin is the bottom of the banner.
  2189. The banner is 248 tall.
  2190. */
  2191. static void misc_ctf_banner_think (edict_t *ent)
  2192. {
  2193. ent->s.frame = (ent->s.frame + 1) % 16;
  2194. ent->nextthink = level.time + FRAMETIME;
  2195. }
  2196. void SP_misc_ctf_banner (edict_t *ent)
  2197. {
  2198. ent->movetype = MOVETYPE_NONE;
  2199. ent->solid = SOLID_NOT;
  2200. ent->s.modelindex = gi.modelindex ("models/ctf/banner/tris.md2");
  2201. if (ent->spawnflags & 1) // team2
  2202. ent->s.skinnum = 1;
  2203. ent->s.frame = rand() % 16;
  2204. gi.linkentity (ent);
  2205. ent->think = misc_ctf_banner_think;
  2206. ent->nextthink = level.time + FRAMETIME;
  2207. }
  2208. /*QUAKED misc_ctf_small_banner (1 .5 0) (-4 -32 0) (4 32 124) TEAM2
  2209. The origin is the bottom of the banner.
  2210. The banner is 124 tall.
  2211. */
  2212. void SP_misc_ctf_small_banner (edict_t *ent)
  2213. {
  2214. ent->movetype = MOVETYPE_NONE;
  2215. ent->solid = SOLID_NOT;
  2216. ent->s.modelindex = gi.modelindex ("models/ctf/banner/small.md2");
  2217. if (ent->spawnflags & 1) // team2
  2218. ent->s.skinnum = 1;
  2219. ent->s.frame = rand() % 16;
  2220. gi.linkentity (ent);
  2221. ent->think = misc_ctf_banner_think;
  2222. ent->nextthink = level.time + FRAMETIME;
  2223. }
  2224. /*-----------------------------------------------------------------------*/
  2225. static void SetLevelName(pmenu_t *p)
  2226. {
  2227. static char levelname[33];
  2228. levelname[0] = '*';
  2229. if (g_edicts[0].message)
  2230. strncpy(levelname+1, g_edicts[0].message, sizeof(levelname) - 2);
  2231. else
  2232. strncpy(levelname+1, level.mapname, sizeof(levelname) - 2);
  2233. levelname[sizeof(levelname) - 1] = 0;
  2234. p->text = levelname;
  2235. }
  2236. /*-----------------------------------------------------------------------*/
  2237. /* ELECTIONS */
  2238. qboolean CTFBeginElection(edict_t *ent, elect_t type, char *msg)
  2239. {
  2240. int i;
  2241. int count;
  2242. edict_t *e;
  2243. if (electpercentage->value == 0) {
  2244. gi.cprintf(ent, PRINT_HIGH, "Elections are disabled, only an admin can process this action.\n");
  2245. return false;
  2246. }
  2247. if (ctfgame.election != ELECT_NONE) {
  2248. gi.cprintf(ent, PRINT_HIGH, "Election already in progress.\n");
  2249. return false;
  2250. }
  2251. // clear votes
  2252. count = 0;
  2253. for (i = 1; i <= maxclients->value; i++) {
  2254. e = g_edicts + i;
  2255. e->client->resp.voted = false;
  2256. if (e->inuse)
  2257. count++;
  2258. }
  2259. if (count < 2) {
  2260. gi.cprintf(ent, PRINT_HIGH, "Not enough players for election.\n");
  2261. return false;
  2262. }
  2263. ctfgame.etarget = ent;
  2264. ctfgame.election = type;
  2265. ctfgame.evotes = 0;
  2266. ctfgame.needvotes = (count * electpercentage->value) / 100;
  2267. ctfgame.electtime = level.time + 20; // twenty seconds for election
  2268. strncpy(ctfgame.emsg, msg, sizeof(ctfgame.emsg) - 1);
  2269. // tell everyone
  2270. gi.bprintf(PRINT_CHAT, "%s\n", ctfgame.emsg);
  2271. gi.bprintf(PRINT_HIGH, "Type YES or NO to vote on this request.\n");
  2272. gi.bprintf(PRINT_HIGH, "Votes: %d Needed: %d Time left: %ds\n", ctfgame.evotes, ctfgame.needvotes,
  2273. (int)(ctfgame.electtime - level.time));
  2274. return true;
  2275. }
  2276. void DoRespawn (edict_t *ent);
  2277. void CTFResetAllPlayers(void)
  2278. {
  2279. int i;
  2280. edict_t *ent;
  2281. for (i = 1; i <= maxclients->value; i++) {
  2282. ent = g_edicts + i;
  2283. if (!ent->inuse)
  2284. continue;
  2285. if (ent->client->menu)
  2286. PMenu_Close(ent);
  2287. CTFPlayerResetGrapple(ent);
  2288. CTFDeadDropFlag(ent);
  2289. CTFDeadDropTech(ent);
  2290. ent->client->resp.ctf_team = CTF_NOTEAM;
  2291. ent->client->resp.ready = false;
  2292. ent->svflags = 0;
  2293. ent->flags &= ~FL_GODMODE;
  2294. PutClientInServer(ent);
  2295. }
  2296. // reset the level
  2297. CTFResetTech();
  2298. CTFResetFlags();
  2299. for (ent = g_edicts + 1, i = 1; i < globals.num_edicts; i++, ent++) {
  2300. if (ent->inuse && !ent->client) {
  2301. if (ent->solid == SOLID_NOT && ent->think == DoRespawn &&
  2302. ent->nextthink >= level.time) {
  2303. ent->nextthink = 0;
  2304. DoRespawn(ent);
  2305. }
  2306. }
  2307. }
  2308. if (ctfgame.match == MATCH_SETUP)
  2309. ctfgame.matchtime = level.time + matchsetuptime->value * 60;
  2310. }
  2311. void CTFAssignGhost(edict_t *ent)
  2312. {
  2313. int ghost, i;
  2314. for (ghost = 0; ghost < MAX_CLIENTS; ghost++)
  2315. if (!ctfgame.ghosts[ghost].code)
  2316. break;
  2317. if (ghost == MAX_CLIENTS)
  2318. return;
  2319. ctfgame.ghosts[ghost].team = ent->client->resp.ctf_team;
  2320. ctfgame.ghosts[ghost].score = 0;
  2321. for (;;) {
  2322. ctfgame.ghosts[ghost].code = 10000 + (rand() % 90000);
  2323. for (i = 0; i < MAX_CLIENTS; i++)
  2324. if (i != ghost && ctfgame.ghosts[i].code == ctfgame.ghosts[ghost].code)
  2325. break;
  2326. if (i == MAX_CLIENTS)
  2327. break;
  2328. }
  2329. ctfgame.ghosts[ghost].ent = ent;
  2330. strcpy(ctfgame.ghosts[ghost].netname, ent->client->pers.netname);
  2331. ent->client->resp.ghost = ctfgame.ghosts + ghost;
  2332. gi.cprintf(ent, PRINT_CHAT, "Your ghost code is **** %d ****\n", ctfgame.ghosts[ghost].code);
  2333. gi.cprintf(ent, PRINT_HIGH, "If you lose connection, you can rejoin with your score "
  2334. "intact by typing \"ghost %d\".\n", ctfgame.ghosts[ghost].code);
  2335. }
  2336. // start a match
  2337. void CTFStartMatch(void)
  2338. {
  2339. int i;
  2340. edict_t *ent;
  2341. int ghost = 0;
  2342. ctfgame.match = MATCH_GAME;
  2343. ctfgame.matchtime = level.time + matchtime->value * 60;
  2344. ctfgame.countdown = false;
  2345. ctfgame.team1 = ctfgame.team2 = 0;
  2346. memset(ctfgame.ghosts, 0, sizeof(ctfgame.ghosts));
  2347. for (i = 1; i <= maxclients->value; i++) {
  2348. ent = g_edicts + i;
  2349. if (!ent->inuse)
  2350. continue;
  2351. ent->client->resp.score = 0;
  2352. ent->client->resp.ctf_state = 0;
  2353. ent->client->resp.ghost = NULL;
  2354. gi.centerprintf(ent, "******************\n\nMATCH HAS STARTED!\n\n******************");
  2355. if (ent->client->resp.ctf_team != CTF_NOTEAM) {
  2356. // make up a ghost code
  2357. CTFAssignGhost(ent);
  2358. CTFPlayerResetGrapple(ent);
  2359. ent->svflags = SVF_NOCLIENT;
  2360. ent->flags &= ~FL_GODMODE;
  2361. ent->client->respawn_time = level.time + 1.0 + ((rand()%30)/10.0);
  2362. ent->client->ps.pmove.pm_type = PM_DEAD;
  2363. ent->client->anim_priority = ANIM_DEATH;
  2364. ent->s.frame = FRAME_death308-1;
  2365. ent->client->anim_end = FRAME_death308;
  2366. ent->deadflag = DEAD_DEAD;
  2367. ent->movetype = MOVETYPE_NOCLIP;
  2368. ent->client->ps.gunindex = 0;
  2369. gi.linkentity (ent);
  2370. }
  2371. }
  2372. }
  2373. void CTFEndMatch(void)
  2374. {
  2375. ctfgame.match = MATCH_POST;
  2376. gi.bprintf(PRINT_CHAT, "MATCH COMPLETED!\n");
  2377. CTFCalcScores();
  2378. gi.bprintf(PRINT_HIGH, "RED TEAM: %d captures, %d points\n",
  2379. ctfgame.team1, ctfgame.total1);
  2380. gi.bprintf(PRINT_HIGH, "BLUE TEAM: %d captures, %d points\n",
  2381. ctfgame.team2, ctfgame.total2);
  2382. if (ctfgame.team1 > ctfgame.team2)
  2383. gi.bprintf(PRINT_CHAT, "RED team won over the BLUE team by %d CAPTURES!\n",
  2384. ctfgame.team1 - ctfgame.team2);
  2385. else if (ctfgame.team2 > ctfgame.team1)
  2386. gi.bprintf(PRINT_CHAT, "BLUE team won over the RED team by %d CAPTURES!\n",
  2387. ctfgame.team2 - ctfgame.team1);
  2388. else if (ctfgame.total1 > ctfgame.total2) // frag tie breaker
  2389. gi.bprintf(PRINT_CHAT, "RED team won over the BLUE team by %d POINTS!\n",
  2390. ctfgame.total1 - ctfgame.total2);
  2391. else if (ctfgame.total2 > ctfgame.total1)
  2392. gi.bprintf(PRINT_CHAT, "BLUE team won over the RED team by %d POINTS!\n",
  2393. ctfgame.total2 - ctfgame.total1);
  2394. else
  2395. gi.bprintf(PRINT_CHAT, "TIE GAME!\n");
  2396. EndDMLevel();
  2397. }
  2398. qboolean CTFNextMap(void)
  2399. {
  2400. if (ctfgame.match == MATCH_POST) {
  2401. ctfgame.match = MATCH_SETUP;
  2402. CTFResetAllPlayers();
  2403. return true;
  2404. }
  2405. return false;
  2406. }
  2407. void CTFWinElection(void)
  2408. {
  2409. switch (ctfgame.election) {
  2410. case ELECT_MATCH :
  2411. // reset into match mode
  2412. if (competition->value < 3)
  2413. gi.cvar_set("competition", "2");
  2414. ctfgame.match = MATCH_SETUP;
  2415. CTFResetAllPlayers();
  2416. break;
  2417. case ELECT_ADMIN :
  2418. ctfgame.etarget->client->resp.admin = true;
  2419. gi.bprintf(PRINT_HIGH, "%s has become an admin.\n", ctfgame.etarget->client->pers.netname);
  2420. gi.cprintf(ctfgame.etarget, PRINT_HIGH, "Type 'admin' to access the adminstration menu.\n");
  2421. break;
  2422. case ELECT_MAP :
  2423. gi.bprintf(PRINT_HIGH, "%s is warping to level %s.\n",
  2424. ctfgame.etarget->client->pers.netname, ctfgame.elevel);
  2425. strncpy(level.forcemap, ctfgame.elevel, sizeof(level.forcemap) - 1);
  2426. EndDMLevel();
  2427. break;
  2428. }
  2429. ctfgame.election = ELECT_NONE;
  2430. }
  2431. void CTFVoteYes(edict_t *ent)
  2432. {
  2433. if (ctfgame.election == ELECT_NONE) {
  2434. gi.cprintf(ent, PRINT_HIGH, "No election is in progress.\n");
  2435. return;
  2436. }
  2437. if (ent->client->resp.voted) {
  2438. gi.cprintf(ent, PRINT_HIGH, "You already voted.\n");
  2439. return;
  2440. }
  2441. if (ctfgame.etarget == ent) {
  2442. gi.cprintf(ent, PRINT_HIGH, "You can't vote for yourself.\n");
  2443. return;
  2444. }
  2445. ent->client->resp.voted = true;
  2446. ctfgame.evotes++;
  2447. if (ctfgame.evotes == ctfgame.needvotes) {
  2448. // the election has been won
  2449. CTFWinElection();
  2450. return;
  2451. }
  2452. gi.bprintf(PRINT_HIGH, "%s\n", ctfgame.emsg);
  2453. gi.bprintf(PRINT_CHAT, "Votes: %d Needed: %d Time left: %ds\n", ctfgame.evotes, ctfgame.needvotes,
  2454. (int)(ctfgame.electtime - level.time));
  2455. }
  2456. void CTFVoteNo(edict_t *ent)
  2457. {
  2458. if (ctfgame.election == ELECT_NONE) {
  2459. gi.cprintf(ent, PRINT_HIGH, "No election is in progress.\n");
  2460. return;
  2461. }
  2462. if (ent->client->resp.voted) {
  2463. gi.cprintf(ent, PRINT_HIGH, "You already voted.\n");
  2464. return;
  2465. }
  2466. if (ctfgame.etarget == ent) {
  2467. gi.cprintf(ent, PRINT_HIGH, "You can't vote for yourself.\n");
  2468. return;
  2469. }
  2470. ent->client->resp.voted = true;
  2471. gi.bprintf(PRINT_HIGH, "%s\n", ctfgame.emsg);
  2472. gi.bprintf(PRINT_CHAT, "Votes: %d Needed: %d Time left: %ds\n", ctfgame.evotes, ctfgame.needvotes,
  2473. (int)(ctfgame.electtime - level.time));
  2474. }
  2475. void CTFReady(edict_t *ent)
  2476. {
  2477. int i, j;
  2478. edict_t *e;
  2479. int t1, t2;
  2480. if (ent->client->resp.ctf_team == CTF_NOTEAM) {
  2481. gi.cprintf(ent, PRINT_HIGH, "Pick a team first (hit <TAB> for menu)\n");
  2482. return;
  2483. }
  2484. if (ctfgame.match != MATCH_SETUP) {
  2485. gi.cprintf(ent, PRINT_HIGH, "A match is not being setup.\n");
  2486. return;
  2487. }
  2488. if (ent->client->resp.ready) {
  2489. gi.cprintf(ent, PRINT_HIGH, "You have already commited.\n");
  2490. return;
  2491. }
  2492. ent->client->resp.ready = true;
  2493. gi.bprintf(PRINT_HIGH, "%s is ready.\n", ent->client->pers.netname);
  2494. t1 = t2 = 0;
  2495. for (j = 0, i = 1; i <= maxclients->value; i++) {
  2496. e = g_edicts + i;
  2497. if (!e->inuse)
  2498. continue;
  2499. if (e->client->resp.ctf_team != CTF_NOTEAM && !e->client->resp.ready)
  2500. j++;
  2501. if (e->client->resp.ctf_team == CTF_TEAM1)
  2502. t1++;
  2503. else if (e->client->resp.ctf_team == CTF_TEAM2)
  2504. t2++;
  2505. }
  2506. if (!j && t1 && t2) {
  2507. // everyone has commited
  2508. gi.bprintf(PRINT_CHAT, "All players have commited. Match starting\n");
  2509. ctfgame.match = MATCH_PREGAME;
  2510. ctfgame.matchtime = level.time + matchstarttime->value;
  2511. ctfgame.countdown = false;
  2512. gi.positioned_sound (world->s.origin, world, CHAN_AUTO | CHAN_RELIABLE, gi.soundindex("misc/talk1.wav"), 1, ATTN_NONE, 0);
  2513. }
  2514. }
  2515. void CTFNotReady(edict_t *ent)
  2516. {
  2517. if (ent->client->resp.ctf_team == CTF_NOTEAM) {
  2518. gi.cprintf(ent, PRINT_HIGH, "Pick a team first (hit <TAB> for menu)\n");
  2519. return;
  2520. }
  2521. if (ctfgame.match != MATCH_SETUP && ctfgame.match != MATCH_PREGAME) {
  2522. gi.cprintf(ent, PRINT_HIGH, "A match is not being setup.\n");
  2523. return;
  2524. }
  2525. if (!ent->client->resp.ready) {
  2526. gi.cprintf(ent, PRINT_HIGH, "You haven't commited.\n");
  2527. return;
  2528. }
  2529. ent->client->resp.ready = false;
  2530. gi.bprintf(PRINT_HIGH, "%s is no longer ready.\n", ent->client->pers.netname);
  2531. if (ctfgame.match == MATCH_PREGAME) {
  2532. gi.bprintf(PRINT_CHAT, "Match halted.\n");
  2533. ctfgame.match = MATCH_SETUP;
  2534. ctfgame.matchtime = level.time + matchsetuptime->value * 60;
  2535. }
  2536. }
  2537. void CTFGhost(edict_t *ent)
  2538. {
  2539. int i;
  2540. int n;
  2541. if (gi.argc() < 2) {
  2542. gi.cprintf(ent, PRINT_HIGH, "Usage: ghost <code>\n");
  2543. return;
  2544. }
  2545. if (ent->client->resp.ctf_team != CTF_NOTEAM) {
  2546. gi.cprintf(ent, PRINT_HIGH, "You are already in the game.\n");
  2547. return;
  2548. }
  2549. if (ctfgame.match != MATCH_GAME) {
  2550. gi.cprintf(ent, PRINT_HIGH, "No match is in progress.\n");
  2551. return;
  2552. }
  2553. n = atoi(gi.argv(1));
  2554. for (i = 0; i < MAX_CLIENTS; i++) {
  2555. if (ctfgame.ghosts[i].code && ctfgame.ghosts[i].code == n) {
  2556. gi.cprintf(ent, PRINT_HIGH, "Ghost code accepted, your position has been reinstated.\n");
  2557. ctfgame.ghosts[i].ent->client->resp.ghost = NULL;
  2558. ent->client->resp.ctf_team = ctfgame.ghosts[i].team;
  2559. ent->client->resp.ghost = ctfgame.ghosts + i;
  2560. ent->client->resp.score = ctfgame.ghosts[i].score;
  2561. ent->client->resp.ctf_state = 0;
  2562. ctfgame.ghosts[i].ent = ent;
  2563. ent->svflags = 0;
  2564. ent->flags &= ~FL_GODMODE;
  2565. PutClientInServer(ent);
  2566. gi.bprintf(PRINT_HIGH, "%s has been reinstated to %s team.\n",
  2567. ent->client->pers.netname, CTFTeamName(ent->client->resp.ctf_team));
  2568. return;
  2569. }
  2570. }
  2571. gi.cprintf(ent, PRINT_HIGH, "Invalid ghost code.\n");
  2572. }
  2573. qboolean CTFMatchSetup(void)
  2574. {
  2575. if (ctfgame.match == MATCH_SETUP || ctfgame.match == MATCH_PREGAME)
  2576. return true;
  2577. return false;
  2578. }
  2579. qboolean CTFMatchOn(void)
  2580. {
  2581. if (ctfgame.match == MATCH_GAME)
  2582. return true;
  2583. return false;
  2584. }
  2585. /*-----------------------------------------------------------------------*/
  2586. void CTFJoinTeam1(edict_t *ent, pmenuhnd_t *p);
  2587. void CTFJoinTeam2(edict_t *ent, pmenuhnd_t *p);
  2588. void CTFCredits(edict_t *ent, pmenuhnd_t *p);
  2589. void CTFReturnToMain(edict_t *ent, pmenuhnd_t *p);
  2590. void CTFChaseCam(edict_t *ent, pmenuhnd_t *p);
  2591. pmenu_t creditsmenu[] = {
  2592. { "*Quake II", PMENU_ALIGN_CENTER, NULL },
  2593. { "*ThreeWave Capture the Flag", PMENU_ALIGN_CENTER, NULL },
  2594. { NULL, PMENU_ALIGN_CENTER, NULL },
  2595. { "*Programming", PMENU_ALIGN_CENTER, NULL },
  2596. { "Dave 'Zoid' Kirsch", PMENU_ALIGN_CENTER, NULL },
  2597. { "*Level Design", PMENU_ALIGN_CENTER, NULL },
  2598. { "Christian Antkow", PMENU_ALIGN_CENTER, NULL },
  2599. { "Tim Willits", PMENU_ALIGN_CENTER, NULL },
  2600. { "Dave 'Zoid' Kirsch", PMENU_ALIGN_CENTER, NULL },
  2601. { "*Art", PMENU_ALIGN_CENTER, NULL },
  2602. { "Adrian Carmack Paul Steed", PMENU_ALIGN_CENTER, NULL },
  2603. { "Kevin Cloud", PMENU_ALIGN_CENTER, NULL },
  2604. { "*Sound", PMENU_ALIGN_CENTER, NULL },
  2605. { "Tom 'Bjorn' Klok", PMENU_ALIGN_CENTER, NULL },
  2606. { "*Original CTF Art Design", PMENU_ALIGN_CENTER, NULL },
  2607. { "Brian 'Whaleboy' Cozzens", PMENU_ALIGN_CENTER, NULL },
  2608. { NULL, PMENU_ALIGN_CENTER, NULL },
  2609. { "Return to Main Menu", PMENU_ALIGN_LEFT, CTFReturnToMain }
  2610. };
  2611. static const int jmenu_level = 2;
  2612. static const int jmenu_match = 3;
  2613. static const int jmenu_red = 5;
  2614. static const int jmenu_blue = 7;
  2615. static const int jmenu_chase = 9;
  2616. static const int jmenu_reqmatch = 11;
  2617. pmenu_t joinmenu[] = {
  2618. { "*Quake II", PMENU_ALIGN_CENTER, NULL },
  2619. { "*ThreeWave Capture the Flag", PMENU_ALIGN_CENTER, NULL },
  2620. { NULL, PMENU_ALIGN_CENTER, NULL },
  2621. { NULL, PMENU_ALIGN_CENTER, NULL },
  2622. { NULL, PMENU_ALIGN_CENTER, NULL },
  2623. { "Join Red Team", PMENU_ALIGN_LEFT, CTFJoinTeam1 },
  2624. { NULL, PMENU_ALIGN_LEFT, NULL },
  2625. { "Join Blue Team", PMENU_ALIGN_LEFT, CTFJoinTeam2 },
  2626. { NULL, PMENU_ALIGN_LEFT, NULL },
  2627. { "Chase Camera", PMENU_ALIGN_LEFT, CTFChaseCam },
  2628. { "Credits", PMENU_ALIGN_LEFT, CTFCredits },
  2629. { NULL, PMENU_ALIGN_LEFT, NULL },
  2630. { NULL, PMENU_ALIGN_LEFT, NULL },
  2631. { "Use [ and ] to move cursor", PMENU_ALIGN_LEFT, NULL },
  2632. { "ENTER to select", PMENU_ALIGN_LEFT, NULL },
  2633. { "ESC to Exit Menu", PMENU_ALIGN_LEFT, NULL },
  2634. { "(TAB to Return)", PMENU_ALIGN_LEFT, NULL },
  2635. { "v" CTF_STRING_VERSION, PMENU_ALIGN_RIGHT, NULL },
  2636. };
  2637. pmenu_t nochasemenu[] = {
  2638. { "*Quake II", PMENU_ALIGN_CENTER, NULL },
  2639. { "*ThreeWave Capture the Flag", PMENU_ALIGN_CENTER, NULL },
  2640. { NULL, PMENU_ALIGN_CENTER, NULL },
  2641. { NULL, PMENU_ALIGN_CENTER, NULL },
  2642. { "No one to chase", PMENU_ALIGN_LEFT, NULL },
  2643. { NULL, PMENU_ALIGN_CENTER, NULL },
  2644. { "Return to Main Menu", PMENU_ALIGN_LEFT, CTFReturnToMain }
  2645. };
  2646. void CTFJoinTeam(edict_t *ent, int desired_team)
  2647. {
  2648. char *s;
  2649. PMenu_Close(ent);
  2650. ent->svflags &= ~SVF_NOCLIENT;
  2651. ent->client->resp.ctf_team = desired_team;
  2652. ent->client->resp.ctf_state = 0;
  2653. s = Info_ValueForKey (ent->client->pers.userinfo, "skin");
  2654. CTFAssignSkin(ent, s);
  2655. // assign a ghost if we are in match mode
  2656. if (ctfgame.match == MATCH_GAME) {
  2657. if (ent->client->resp.ghost)
  2658. ent->client->resp.ghost->code = 0;
  2659. ent->client->resp.ghost = NULL;
  2660. CTFAssignGhost(ent);
  2661. }
  2662. PutClientInServer (ent);
  2663. // add a teleportation effect
  2664. ent->s.event = EV_PLAYER_TELEPORT;
  2665. // hold in place briefly
  2666. ent->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
  2667. ent->client->ps.pmove.pm_time = 14;
  2668. gi.bprintf(PRINT_HIGH, "%s joined the %s team.\n",
  2669. ent->client->pers.netname, CTFTeamName(desired_team));
  2670. if (ctfgame.match == MATCH_SETUP) {
  2671. gi.centerprintf(ent, "***********************\n"
  2672. "Type \"ready\" in console\n"
  2673. "to ready up.\n"
  2674. "***********************");
  2675. }
  2676. }
  2677. void CTFJoinTeam1(edict_t *ent, pmenuhnd_t *p)
  2678. {
  2679. CTFJoinTeam(ent, CTF_TEAM1);
  2680. }
  2681. void CTFJoinTeam2(edict_t *ent, pmenuhnd_t *p)
  2682. {
  2683. CTFJoinTeam(ent, CTF_TEAM2);
  2684. }
  2685. void CTFChaseCam(edict_t *ent, pmenuhnd_t *p)
  2686. {
  2687. int i;
  2688. edict_t *e;
  2689. if (ent->client->chase_target) {
  2690. ent->client->chase_target = NULL;
  2691. ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
  2692. PMenu_Close(ent);
  2693. return;
  2694. }
  2695. for (i = 1; i <= maxclients->value; i++) {
  2696. e = g_edicts + i;
  2697. if (e->inuse && e->solid != SOLID_NOT) {
  2698. ent->client->chase_target = e;
  2699. PMenu_Close(ent);
  2700. ent->client->update_chase = true;
  2701. return;
  2702. }
  2703. }
  2704. SetLevelName(nochasemenu + jmenu_level);
  2705. PMenu_Close(ent);
  2706. PMenu_Open(ent, nochasemenu, -1, sizeof(nochasemenu) / sizeof(pmenu_t), NULL);
  2707. }
  2708. void CTFReturnToMain(edict_t *ent, pmenuhnd_t *p)
  2709. {
  2710. PMenu_Close(ent);
  2711. CTFOpenJoinMenu(ent);
  2712. }
  2713. void CTFRequestMatch(edict_t *ent, pmenuhnd_t *p)
  2714. {
  2715. char text[1024];
  2716. PMenu_Close(ent);
  2717. sprintf(text, "%s has requested to switch to competition mode.",
  2718. ent->client->pers.netname);
  2719. CTFBeginElection(ent, ELECT_MATCH, text);
  2720. }
  2721. void DeathmatchScoreboard (edict_t *ent);
  2722. void CTFShowScores(edict_t *ent, pmenu_t *p)
  2723. {
  2724. PMenu_Close(ent);
  2725. ent->client->showscores = true;
  2726. ent->client->showinventory = false;
  2727. DeathmatchScoreboard (ent);
  2728. }
  2729. int CTFUpdateJoinMenu(edict_t *ent)
  2730. {
  2731. static char team1players[32];
  2732. static char team2players[32];
  2733. int num1, num2, i;
  2734. if (ctfgame.match >= MATCH_PREGAME && matchlock->value) {
  2735. joinmenu[jmenu_red].text = "MATCH IS LOCKED";
  2736. joinmenu[jmenu_red].SelectFunc = NULL;
  2737. joinmenu[jmenu_blue].text = " (entry is not permitted)";
  2738. joinmenu[jmenu_blue].SelectFunc = NULL;
  2739. } else {
  2740. if (ctfgame.match >= MATCH_PREGAME) {
  2741. joinmenu[jmenu_red].text = "Join Red MATCH Team";
  2742. joinmenu[jmenu_blue].text = "Join Blue MATCH Team";
  2743. } else {
  2744. joinmenu[jmenu_red].text = "Join Red Team";
  2745. joinmenu[jmenu_blue].text = "Join Blue Team";
  2746. }
  2747. joinmenu[jmenu_red].SelectFunc = CTFJoinTeam1;
  2748. joinmenu[jmenu_blue].SelectFunc = CTFJoinTeam2;
  2749. }
  2750. if (ctf_forcejoin->string && *ctf_forcejoin->string) {
  2751. if (stricmp(ctf_forcejoin->string, "red") == 0) {
  2752. joinmenu[jmenu_blue].text = NULL;
  2753. joinmenu[jmenu_blue].SelectFunc = NULL;
  2754. } else if (stricmp(ctf_forcejoin->string, "blue") == 0) {
  2755. joinmenu[jmenu_red].text = NULL;
  2756. joinmenu[jmenu_red].SelectFunc = NULL;
  2757. }
  2758. }
  2759. if (ent->client->chase_target)
  2760. joinmenu[jmenu_chase].text = "Leave Chase Camera";
  2761. else
  2762. joinmenu[jmenu_chase].text = "Chase Camera";
  2763. SetLevelName(joinmenu + jmenu_level);
  2764. num1 = num2 = 0;
  2765. for (i = 0; i < maxclients->value; i++) {
  2766. if (!g_edicts[i+1].inuse)
  2767. continue;
  2768. if (game.clients[i].resp.ctf_team == CTF_TEAM1)
  2769. num1++;
  2770. else if (game.clients[i].resp.ctf_team == CTF_TEAM2)
  2771. num2++;
  2772. }
  2773. sprintf(team1players, " (%d players)", num1);
  2774. sprintf(team2players, " (%d players)", num2);
  2775. switch (ctfgame.match) {
  2776. case MATCH_NONE :
  2777. joinmenu[jmenu_match].text = NULL;
  2778. break;
  2779. case MATCH_SETUP :
  2780. joinmenu[jmenu_match].text = "*MATCH SETUP IN PROGRESS";
  2781. break;
  2782. case MATCH_PREGAME :
  2783. joinmenu[jmenu_match].text = "*MATCH STARTING";
  2784. break;
  2785. case MATCH_GAME :
  2786. joinmenu[jmenu_match].text = "*MATCH IN PROGRESS";
  2787. break;
  2788. }
  2789. if (joinmenu[jmenu_red].text)
  2790. joinmenu[jmenu_red+1].text = team1players;
  2791. else
  2792. joinmenu[jmenu_red+1].text = NULL;
  2793. if (joinmenu[jmenu_blue].text)
  2794. joinmenu[jmenu_blue+1].text = team2players;
  2795. else
  2796. joinmenu[jmenu_blue+1].text = NULL;
  2797. joinmenu[jmenu_reqmatch].text = NULL;
  2798. joinmenu[jmenu_reqmatch].SelectFunc = NULL;
  2799. if (competition->value && ctfgame.match < MATCH_SETUP) {
  2800. joinmenu[jmenu_reqmatch].text = "Request Match";
  2801. joinmenu[jmenu_reqmatch].SelectFunc = CTFRequestMatch;
  2802. }
  2803. if (num1 > num2)
  2804. return CTF_TEAM1;
  2805. else if (num2 > num1)
  2806. return CTF_TEAM2;
  2807. return (rand() & 1) ? CTF_TEAM1 : CTF_TEAM2;
  2808. }
  2809. void CTFOpenJoinMenu(edict_t *ent)
  2810. {
  2811. int team;
  2812. team = CTFUpdateJoinMenu(ent);
  2813. if (ent->client->chase_target)
  2814. team = 8;
  2815. else if (team == CTF_TEAM1)
  2816. team = 4;
  2817. else
  2818. team = 6;
  2819. PMenu_Open(ent, joinmenu, team, sizeof(joinmenu) / sizeof(pmenu_t), NULL);
  2820. }
  2821. void CTFCredits(edict_t *ent, pmenuhnd_t *p)
  2822. {
  2823. PMenu_Close(ent);
  2824. PMenu_Open(ent, creditsmenu, -1, sizeof(creditsmenu) / sizeof(pmenu_t), NULL);
  2825. }
  2826. qboolean CTFStartClient(edict_t *ent)
  2827. {
  2828. if (ent->client->resp.ctf_team != CTF_NOTEAM)
  2829. return false;
  2830. if (!((int)dmflags->value & DF_CTF_FORCEJOIN) || ctfgame.match >= MATCH_SETUP) {
  2831. // start as 'observer'
  2832. ent->movetype = MOVETYPE_NOCLIP;
  2833. ent->solid = SOLID_NOT;
  2834. ent->svflags |= SVF_NOCLIENT;
  2835. ent->client->resp.ctf_team = CTF_NOTEAM;
  2836. ent->client->ps.gunindex = 0;
  2837. gi.linkentity (ent);
  2838. CTFOpenJoinMenu(ent);
  2839. return true;
  2840. }
  2841. return false;
  2842. }
  2843. void CTFObserver(edict_t *ent)
  2844. {
  2845. char userinfo[MAX_INFO_STRING];
  2846. // start as 'observer'
  2847. if (ent->movetype == MOVETYPE_NOCLIP)
  2848. CTFPlayerResetGrapple(ent);
  2849. CTFDeadDropFlag(ent);
  2850. CTFDeadDropTech(ent);
  2851. ent->deadflag = DEAD_NO;
  2852. ent->movetype = MOVETYPE_NOCLIP;
  2853. ent->solid = SOLID_NOT;
  2854. ent->svflags |= SVF_NOCLIENT;
  2855. ent->client->resp.ctf_team = CTF_NOTEAM;
  2856. ent->client->ps.gunindex = 0;
  2857. ent->client->resp.score = 0;
  2858. memcpy (userinfo, ent->client->pers.userinfo, sizeof(userinfo));
  2859. InitClientPersistant(ent->client);
  2860. ClientUserinfoChanged (ent, userinfo);
  2861. gi.linkentity (ent);
  2862. CTFOpenJoinMenu(ent);
  2863. }
  2864. qboolean CTFInMatch(void)
  2865. {
  2866. if (ctfgame.match > MATCH_NONE)
  2867. return true;
  2868. return false;
  2869. }
  2870. qboolean CTFCheckRules(void)
  2871. {
  2872. int t;
  2873. int i, j;
  2874. char text[64];
  2875. edict_t *ent;
  2876. if (ctfgame.election != ELECT_NONE && ctfgame.electtime <= level.time) {
  2877. gi.bprintf(PRINT_CHAT, "Election timed out and has been cancelled.\n");
  2878. ctfgame.election = ELECT_NONE;
  2879. }
  2880. if (ctfgame.match != MATCH_NONE) {
  2881. t = ctfgame.matchtime - level.time;
  2882. // no team warnings in match mode
  2883. ctfgame.warnactive = 0;
  2884. if (t <= 0) { // time ended on something
  2885. switch (ctfgame.match) {
  2886. case MATCH_SETUP :
  2887. // go back to normal mode
  2888. if (competition->value < 3) {
  2889. ctfgame.match = MATCH_NONE;
  2890. gi.cvar_set("competition", "1");
  2891. CTFResetAllPlayers();
  2892. } else {
  2893. // reset the time
  2894. ctfgame.matchtime = level.time + matchsetuptime->value * 60;
  2895. }
  2896. return false;
  2897. case MATCH_PREGAME :
  2898. // match started!
  2899. CTFStartMatch();
  2900. gi.positioned_sound (world->s.origin, world, CHAN_AUTO | CHAN_RELIABLE, gi.soundindex("misc/tele_up.wav"), 1, ATTN_NONE, 0);
  2901. return false;
  2902. case MATCH_GAME :
  2903. // match ended!
  2904. CTFEndMatch();
  2905. gi.positioned_sound (world->s.origin, world, CHAN_AUTO | CHAN_RELIABLE, gi.soundindex("misc/bigtele.wav"), 1, ATTN_NONE, 0);
  2906. return false;
  2907. }
  2908. }
  2909. if (t == ctfgame.lasttime)
  2910. return false;
  2911. ctfgame.lasttime = t;
  2912. switch (ctfgame.match) {
  2913. case MATCH_SETUP :
  2914. for (j = 0, i = 1; i <= maxclients->value; i++) {
  2915. ent = g_edicts + i;
  2916. if (!ent->inuse)
  2917. continue;
  2918. if (ent->client->resp.ctf_team != CTF_NOTEAM &&
  2919. !ent->client->resp.ready)
  2920. j++;
  2921. }
  2922. if (competition->value < 3)
  2923. sprintf(text, "%02d:%02d SETUP: %d not ready",
  2924. t / 60, t % 60, j);
  2925. else
  2926. sprintf(text, "SETUP: %d not ready", j);
  2927. gi.configstring (CONFIG_CTF_MATCH, text);
  2928. break;
  2929. case MATCH_PREGAME :
  2930. sprintf(text, "%02d:%02d UNTIL START",
  2931. t / 60, t % 60);
  2932. gi.configstring (CONFIG_CTF_MATCH, text);
  2933. if (t <= 10 && !ctfgame.countdown) {
  2934. ctfgame.countdown = true;
  2935. gi.positioned_sound (world->s.origin, world, CHAN_AUTO | CHAN_RELIABLE, gi.soundindex("world/10_0.wav"), 1, ATTN_NONE, 0);
  2936. }
  2937. break;
  2938. case MATCH_GAME:
  2939. sprintf(text, "%02d:%02d MATCH",
  2940. t / 60, t % 60);
  2941. gi.configstring (CONFIG_CTF_MATCH, text);
  2942. if (t <= 10 && !ctfgame.countdown) {
  2943. ctfgame.countdown = true;
  2944. gi.positioned_sound (world->s.origin, world, CHAN_AUTO | CHAN_RELIABLE, gi.soundindex("world/10_0.wav"), 1, ATTN_NONE, 0);
  2945. }
  2946. break;
  2947. }
  2948. return false;
  2949. } else {
  2950. int team1 = 0, team2 = 0;
  2951. if (level.time == ctfgame.lasttime)
  2952. return false;
  2953. ctfgame.lasttime = level.time;
  2954. // this is only done in non-match (public) mode
  2955. if (warn_unbalanced->value) {
  2956. // count up the team totals
  2957. for (i = 1; i <= maxclients->value; i++) {
  2958. ent = g_edicts + i;
  2959. if (!ent->inuse)
  2960. continue;
  2961. if (ent->client->resp.ctf_team == CTF_TEAM1)
  2962. team1++;
  2963. else if (ent->client->resp.ctf_team == CTF_TEAM2)
  2964. team2++;
  2965. }
  2966. if (team1 - team2 >= 2 && team2 >= 2) {
  2967. if (ctfgame.warnactive != CTF_TEAM1) {
  2968. ctfgame.warnactive = CTF_TEAM1;
  2969. gi.configstring (CONFIG_CTF_TEAMINFO, "WARNING: Red has too many players");
  2970. }
  2971. } else if (team2 - team1 >= 2 && team1 >= 2) {
  2972. if (ctfgame.warnactive != CTF_TEAM2) {
  2973. ctfgame.warnactive = CTF_TEAM2;
  2974. gi.configstring (CONFIG_CTF_TEAMINFO, "WARNING: Blue has too many players");
  2975. }
  2976. } else
  2977. ctfgame.warnactive = 0;
  2978. } else
  2979. ctfgame.warnactive = 0;
  2980. }
  2981. if (capturelimit->value &&
  2982. (ctfgame.team1 >= capturelimit->value ||
  2983. ctfgame.team2 >= capturelimit->value)) {
  2984. gi.bprintf (PRINT_HIGH, "Capturelimit hit.\n");
  2985. return true;
  2986. }
  2987. return false;
  2988. }
  2989. /*--------------------------------------------------------------------------
  2990. * just here to help old map conversions
  2991. *--------------------------------------------------------------------------*/
  2992. static void old_teleporter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  2993. {
  2994. edict_t *dest;
  2995. int i;
  2996. vec3_t forward;
  2997. if (!other->client)
  2998. return;
  2999. dest = G_Find (NULL, FOFS(targetname), self->target);
  3000. if (!dest)
  3001. {
  3002. gi.dprintf ("Couldn't find destination\n");
  3003. return;
  3004. }
  3005. //ZOID
  3006. CTFPlayerResetGrapple(other);
  3007. //ZOID
  3008. // unlink to make sure it can't possibly interfere with KillBox
  3009. gi.unlinkentity (other);
  3010. VectorCopy (dest->s.origin, other->s.origin);
  3011. VectorCopy (dest->s.origin, other->s.old_origin);
  3012. // other->s.origin[2] += 10;
  3013. // clear the velocity and hold them in place briefly
  3014. VectorClear (other->velocity);
  3015. other->client->ps.pmove.pm_time = 160>>3; // hold time
  3016. other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;
  3017. // draw the teleport splash at source and on the player
  3018. self->enemy->s.event = EV_PLAYER_TELEPORT;
  3019. other->s.event = EV_PLAYER_TELEPORT;
  3020. // set angles
  3021. for (i=0 ; i<3 ; i++)
  3022. other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]);
  3023. other->s.angles[PITCH] = 0;
  3024. other->s.angles[YAW] = dest->s.angles[YAW];
  3025. other->s.angles[ROLL] = 0;
  3026. VectorCopy (dest->s.angles, other->client->ps.viewangles);
  3027. VectorCopy (dest->s.angles, other->client->v_angle);
  3028. // give a little forward velocity
  3029. AngleVectors (other->client->v_angle, forward, NULL, NULL);
  3030. VectorScale(forward, 200, other->velocity);
  3031. // kill anything at the destination
  3032. if (!KillBox (other))
  3033. {
  3034. }
  3035. gi.linkentity (other);
  3036. }
  3037. /*QUAKED trigger_teleport (0.5 0.5 0.5) ?
  3038. Players touching this will be teleported
  3039. */
  3040. void SP_trigger_teleport (edict_t *ent)
  3041. {
  3042. edict_t *s;
  3043. int i;
  3044. if (!ent->target)
  3045. {
  3046. gi.dprintf ("teleporter without a target.\n");
  3047. G_FreeEdict (ent);
  3048. return;
  3049. }
  3050. ent->svflags |= SVF_NOCLIENT;
  3051. ent->solid = SOLID_TRIGGER;
  3052. ent->touch = old_teleporter_touch;
  3053. gi.setmodel (ent, ent->model);
  3054. gi.linkentity (ent);
  3055. // noise maker and splash effect dude
  3056. s = G_Spawn();
  3057. ent->enemy = s;
  3058. for (i = 0; i < 3; i++)
  3059. s->s.origin[i] = ent->mins[i] + (ent->maxs[i] - ent->mins[i])/2;
  3060. s->s.sound = gi.soundindex ("world/hum1.wav");
  3061. gi.linkentity(s);
  3062. }
  3063. /*QUAKED info_teleport_destination (0.5 0.5 0.5) (-16 -16 -24) (16 16 32)
  3064. Point trigger_teleports at these.
  3065. */
  3066. void SP_info_teleport_destination (edict_t *ent)
  3067. {
  3068. ent->s.origin[2] += 16;
  3069. }
  3070. /*----------------------------------------------------------------------------------*/
  3071. /* ADMIN */
  3072. typedef struct admin_settings_s {
  3073. int matchlen;
  3074. int matchsetuplen;
  3075. int matchstartlen;
  3076. qboolean weaponsstay;
  3077. qboolean instantitems;
  3078. qboolean quaddrop;
  3079. qboolean instantweap;
  3080. qboolean matchlock;
  3081. } admin_settings_t;
  3082. #define SETMENU_SIZE (7 + 5)
  3083. void CTFAdmin_UpdateSettings(edict_t *ent, pmenuhnd_t *setmenu);
  3084. void CTFOpenAdminMenu(edict_t *ent);
  3085. void CTFAdmin_SettingsApply(edict_t *ent, pmenuhnd_t *p)
  3086. {
  3087. admin_settings_t *settings = p->arg;
  3088. char st[80];
  3089. int i;
  3090. if (settings->matchlen != matchtime->value) {
  3091. gi.bprintf(PRINT_HIGH, "%s changed the match length to %d minutes.\n",
  3092. ent->client->pers.netname, settings->matchlen);
  3093. if (ctfgame.match == MATCH_GAME) {
  3094. // in the middle of a match, change it on the fly
  3095. ctfgame.matchtime = (ctfgame.matchtime - matchtime->value*60) + settings->matchlen*60;
  3096. }
  3097. sprintf(st, "%d", settings->matchlen);
  3098. gi.cvar_set("matchtime", st);
  3099. }
  3100. if (settings->matchsetuplen != matchsetuptime->value) {
  3101. gi.bprintf(PRINT_HIGH, "%s changed the match setup time to %d minutes.\n",
  3102. ent->client->pers.netname, settings->matchsetuplen);
  3103. if (ctfgame.match == MATCH_SETUP) {
  3104. // in the middle of a match, change it on the fly
  3105. ctfgame.matchtime = (ctfgame.matchtime - matchsetuptime->value*60) + settings->matchsetuplen*60;
  3106. }
  3107. sprintf(st, "%d", settings->matchsetuplen);
  3108. gi.cvar_set("matchsetuptime", st);
  3109. }
  3110. if (settings->matchstartlen != matchstarttime->value) {
  3111. gi.bprintf(PRINT_HIGH, "%s changed the match start time to %d seconds.\n",
  3112. ent->client->pers.netname, settings->matchstartlen);
  3113. if (ctfgame.match == MATCH_PREGAME) {
  3114. // in the middle of a match, change it on the fly
  3115. ctfgame.matchtime = (ctfgame.matchtime - matchstarttime->value) + settings->matchstartlen;
  3116. }
  3117. sprintf(st, "%d", settings->matchstartlen);
  3118. gi.cvar_set("matchstarttime", st);
  3119. }
  3120. if (settings->weaponsstay != !!((int)dmflags->value & DF_WEAPONS_STAY)) {
  3121. gi.bprintf(PRINT_HIGH, "%s turned %s weapons stay.\n",
  3122. ent->client->pers.netname, settings->weaponsstay ? "on" : "off");
  3123. i = (int)dmflags->value;
  3124. if (settings->weaponsstay)
  3125. i |= DF_WEAPONS_STAY;
  3126. else
  3127. i &= ~DF_WEAPONS_STAY;
  3128. sprintf(st, "%d", i);
  3129. gi.cvar_set("dmflags", st);
  3130. }
  3131. if (settings->instantitems != !!((int)dmflags->value & DF_INSTANT_ITEMS)) {
  3132. gi.bprintf(PRINT_HIGH, "%s turned %s instant items.\n",
  3133. ent->client->pers.netname, settings->instantitems ? "on" : "off");
  3134. i = (int)dmflags->value;
  3135. if (settings->instantitems)
  3136. i |= DF_INSTANT_ITEMS;
  3137. else
  3138. i &= ~DF_INSTANT_ITEMS;
  3139. sprintf(st, "%d", i);
  3140. gi.cvar_set("dmflags", st);
  3141. }
  3142. if (settings->quaddrop != !!((int)dmflags->value & DF_QUAD_DROP)) {
  3143. gi.bprintf(PRINT_HIGH, "%s turned %s quad drop.\n",
  3144. ent->client->pers.netname, settings->quaddrop ? "on" : "off");
  3145. i = (int)dmflags->value;
  3146. if (settings->quaddrop)
  3147. i |= DF_QUAD_DROP;
  3148. else
  3149. i &= ~DF_QUAD_DROP;
  3150. sprintf(st, "%d", i);
  3151. gi.cvar_set("dmflags", st);
  3152. }
  3153. if (settings->instantweap != !!((int)instantweap->value)) {
  3154. gi.bprintf(PRINT_HIGH, "%s turned %s instant weapons.\n",
  3155. ent->client->pers.netname, settings->instantweap ? "on" : "off");
  3156. sprintf(st, "%d", (int)settings->instantweap);
  3157. gi.cvar_set("instantweap", st);
  3158. }
  3159. if (settings->matchlock != !!((int)matchlock->value)) {
  3160. gi.bprintf(PRINT_HIGH, "%s turned %s match lock.\n",
  3161. ent->client->pers.netname, settings->matchlock ? "on" : "off");
  3162. sprintf(st, "%d", (int)settings->matchlock);
  3163. gi.cvar_set("matchlock", st);
  3164. }
  3165. PMenu_Close(ent);
  3166. CTFOpenAdminMenu(ent);
  3167. }
  3168. void CTFAdmin_SettingsCancel(edict_t *ent, pmenuhnd_t *p)
  3169. {
  3170. admin_settings_t *settings = p->arg;
  3171. PMenu_Close(ent);
  3172. CTFOpenAdminMenu(ent);
  3173. }
  3174. void CTFAdmin_ChangeMatchLen(edict_t *ent, pmenuhnd_t *p)
  3175. {
  3176. admin_settings_t *settings = p->arg;
  3177. settings->matchlen = (settings->matchlen % 60) + 5;
  3178. if (settings->matchlen < 5)
  3179. settings->matchlen = 5;
  3180. CTFAdmin_UpdateSettings(ent, p);
  3181. }
  3182. void CTFAdmin_ChangeMatchSetupLen(edict_t *ent, pmenuhnd_t *p)
  3183. {
  3184. admin_settings_t *settings = p->arg;
  3185. settings->matchsetuplen = (settings->matchsetuplen % 60) + 5;
  3186. if (settings->matchsetuplen < 5)
  3187. settings->matchsetuplen = 5;
  3188. CTFAdmin_UpdateSettings(ent, p);
  3189. }
  3190. void CTFAdmin_ChangeMatchStartLen(edict_t *ent, pmenuhnd_t *p)
  3191. {
  3192. admin_settings_t *settings = p->arg;
  3193. settings->matchstartlen = (settings->matchstartlen % 600) + 10;
  3194. if (settings->matchstartlen < 20)
  3195. settings->matchstartlen = 20;
  3196. CTFAdmin_UpdateSettings(ent, p);
  3197. }
  3198. void CTFAdmin_ChangeWeapStay(edict_t *ent, pmenuhnd_t *p)
  3199. {
  3200. admin_settings_t *settings = p->arg;
  3201. settings->weaponsstay = !settings->weaponsstay;
  3202. CTFAdmin_UpdateSettings(ent, p);
  3203. }
  3204. void CTFAdmin_ChangeInstantItems(edict_t *ent, pmenuhnd_t *p)
  3205. {
  3206. admin_settings_t *settings = p->arg;
  3207. settings->instantitems = !settings->instantitems;
  3208. CTFAdmin_UpdateSettings(ent, p);
  3209. }
  3210. void CTFAdmin_ChangeQuadDrop(edict_t *ent, pmenuhnd_t *p)
  3211. {
  3212. admin_settings_t *settings = p->arg;
  3213. settings->quaddrop = !settings->quaddrop;
  3214. CTFAdmin_UpdateSettings(ent, p);
  3215. }
  3216. void CTFAdmin_ChangeInstantWeap(edict_t *ent, pmenuhnd_t *p)
  3217. {
  3218. admin_settings_t *settings = p->arg;
  3219. settings->instantweap = !settings->instantweap;
  3220. CTFAdmin_UpdateSettings(ent, p);
  3221. }
  3222. void CTFAdmin_ChangeMatchLock(edict_t *ent, pmenuhnd_t *p)
  3223. {
  3224. admin_settings_t *settings = p->arg;
  3225. settings->matchlock = !settings->matchlock;
  3226. CTFAdmin_UpdateSettings(ent, p);
  3227. }
  3228. void CTFAdmin_UpdateSettings(edict_t *ent, pmenuhnd_t *setmenu)
  3229. {
  3230. int i = 2;
  3231. char text[64];
  3232. admin_settings_t *settings = setmenu->arg;
  3233. sprintf(text, "Match Len: %2d mins", settings->matchlen);
  3234. PMenu_UpdateEntry(setmenu->entries + i, text, PMENU_ALIGN_LEFT, CTFAdmin_ChangeMatchLen);
  3235. i++;
  3236. sprintf(text, "Match Setup Len: %2d mins", settings->matchsetuplen);
  3237. PMenu_UpdateEntry(setmenu->entries + i, text, PMENU_ALIGN_LEFT, CTFAdmin_ChangeMatchSetupLen);
  3238. i++;
  3239. sprintf(text, "Match Start Len: %2d secs", settings->matchstartlen);
  3240. PMenu_UpdateEntry(setmenu->entries + i, text, PMENU_ALIGN_LEFT, CTFAdmin_ChangeMatchStartLen);
  3241. i++;
  3242. sprintf(text, "Weapons Stay: %s", settings->weaponsstay ? "Yes" : "No");
  3243. PMenu_UpdateEntry(setmenu->entries + i, text, PMENU_ALIGN_LEFT, CTFAdmin_ChangeWeapStay);
  3244. i++;
  3245. sprintf(text, "Instant Items: %s", settings->instantitems ? "Yes" : "No");
  3246. PMenu_UpdateEntry(setmenu->entries + i, text, PMENU_ALIGN_LEFT, CTFAdmin_ChangeInstantItems);
  3247. i++;
  3248. sprintf(text, "Quad Drop: %s", settings->quaddrop ? "Yes" : "No");
  3249. PMenu_UpdateEntry(setmenu->entries + i, text, PMENU_ALIGN_LEFT, CTFAdmin_ChangeQuadDrop);
  3250. i++;
  3251. sprintf(text, "Instant Weapons: %s", settings->instantweap ? "Yes" : "No");
  3252. PMenu_UpdateEntry(setmenu->entries + i, text, PMENU_ALIGN_LEFT, CTFAdmin_ChangeInstantWeap);
  3253. i++;
  3254. sprintf(text, "Match Lock: %s", settings->matchlock ? "Yes" : "No");
  3255. PMenu_UpdateEntry(setmenu->entries + i, text, PMENU_ALIGN_LEFT, CTFAdmin_ChangeMatchLock);
  3256. i++;
  3257. PMenu_Update(ent);
  3258. }
  3259. pmenu_t def_setmenu[] = {
  3260. { "*Settings Menu", PMENU_ALIGN_CENTER, NULL },
  3261. { NULL, PMENU_ALIGN_CENTER, NULL },
  3262. { NULL, PMENU_ALIGN_LEFT, NULL }, //int matchlen;
  3263. { NULL, PMENU_ALIGN_LEFT, NULL }, //int matchsetuplen;
  3264. { NULL, PMENU_ALIGN_LEFT, NULL }, //int matchstartlen;
  3265. { NULL, PMENU_ALIGN_LEFT, NULL }, //qboolean weaponsstay;
  3266. { NULL, PMENU_ALIGN_LEFT, NULL }, //qboolean instantitems;
  3267. { NULL, PMENU_ALIGN_LEFT, NULL }, //qboolean quaddrop;
  3268. { NULL, PMENU_ALIGN_LEFT, NULL }, //qboolean instantweap;
  3269. { NULL, PMENU_ALIGN_LEFT, NULL }, //qboolean matchlock;
  3270. { NULL, PMENU_ALIGN_LEFT, NULL },
  3271. { "Apply", PMENU_ALIGN_LEFT, CTFAdmin_SettingsApply },
  3272. { "Cancel", PMENU_ALIGN_LEFT, CTFAdmin_SettingsCancel }
  3273. };
  3274. void CTFAdmin_Settings(edict_t *ent, pmenuhnd_t *p)
  3275. {
  3276. admin_settings_t *settings;
  3277. pmenuhnd_t *menu;
  3278. PMenu_Close(ent);
  3279. settings = malloc(sizeof(*settings));
  3280. settings->matchlen = matchtime->value;
  3281. settings->matchsetuplen = matchsetuptime->value;
  3282. settings->matchstartlen = matchstarttime->value;
  3283. settings->weaponsstay = !!((int)dmflags->value & DF_WEAPONS_STAY);
  3284. settings->instantitems = !!((int)dmflags->value & DF_INSTANT_ITEMS);
  3285. settings->quaddrop = !!((int)dmflags->value & DF_QUAD_DROP);
  3286. settings->instantweap = instantweap->value != 0;
  3287. settings->matchlock = matchlock->value != 0;
  3288. menu = PMenu_Open(ent, def_setmenu, -1, sizeof(def_setmenu) / sizeof(pmenu_t), settings);
  3289. CTFAdmin_UpdateSettings(ent, menu);
  3290. }
  3291. void CTFAdmin_MatchSet(edict_t *ent, pmenuhnd_t *p)
  3292. {
  3293. PMenu_Close(ent);
  3294. if (ctfgame.match == MATCH_SETUP) {
  3295. gi.bprintf(PRINT_CHAT, "Match has been forced to start.\n");
  3296. ctfgame.match = MATCH_PREGAME;
  3297. ctfgame.matchtime = level.time + matchstarttime->value;
  3298. gi.positioned_sound (world->s.origin, world, CHAN_AUTO | CHAN_RELIABLE, gi.soundindex("misc/talk1.wav"), 1, ATTN_NONE, 0);
  3299. ctfgame.countdown = false;
  3300. } else if (ctfgame.match == MATCH_GAME) {
  3301. gi.bprintf(PRINT_CHAT, "Match has been forced to terminate.\n");
  3302. ctfgame.match = MATCH_SETUP;
  3303. ctfgame.matchtime = level.time + matchsetuptime->value * 60;
  3304. CTFResetAllPlayers();
  3305. }
  3306. }
  3307. void CTFAdmin_MatchMode(edict_t *ent, pmenuhnd_t *p)
  3308. {
  3309. PMenu_Close(ent);
  3310. if (ctfgame.match != MATCH_SETUP) {
  3311. if (competition->value < 3)
  3312. gi.cvar_set("competition", "2");
  3313. ctfgame.match = MATCH_SETUP;
  3314. CTFResetAllPlayers();
  3315. }
  3316. }
  3317. void CTFAdmin_Reset(edict_t *ent, pmenuhnd_t *p)
  3318. {
  3319. PMenu_Close(ent);
  3320. // go back to normal mode
  3321. gi.bprintf(PRINT_CHAT, "Match mode has been terminated, reseting to normal game.\n");
  3322. ctfgame.match = MATCH_NONE;
  3323. gi.cvar_set("competition", "1");
  3324. CTFResetAllPlayers();
  3325. }
  3326. void CTFAdmin_Cancel(edict_t *ent, pmenuhnd_t *p)
  3327. {
  3328. PMenu_Close(ent);
  3329. }
  3330. pmenu_t adminmenu[] = {
  3331. { "*Administration Menu", PMENU_ALIGN_CENTER, NULL },
  3332. { NULL, PMENU_ALIGN_CENTER, NULL }, // blank
  3333. { "Settings", PMENU_ALIGN_LEFT, CTFAdmin_Settings },
  3334. { NULL, PMENU_ALIGN_LEFT, NULL },
  3335. { NULL, PMENU_ALIGN_LEFT, NULL },
  3336. { "Cancel", PMENU_ALIGN_LEFT, CTFAdmin_Cancel },
  3337. { NULL, PMENU_ALIGN_CENTER, NULL },
  3338. };
  3339. void CTFOpenAdminMenu(edict_t *ent)
  3340. {
  3341. adminmenu[3].text = NULL;
  3342. adminmenu[3].SelectFunc = NULL;
  3343. adminmenu[4].text = NULL;
  3344. adminmenu[4].SelectFunc = NULL;
  3345. if (ctfgame.match == MATCH_SETUP) {
  3346. adminmenu[3].text = "Force start match";
  3347. adminmenu[3].SelectFunc = CTFAdmin_MatchSet;
  3348. adminmenu[4].text = "Reset to pickup mode";
  3349. adminmenu[4].SelectFunc = CTFAdmin_Reset;
  3350. } else if (ctfgame.match == MATCH_GAME || ctfgame.match == MATCH_PREGAME) {
  3351. adminmenu[3].text = "Cancel match";
  3352. adminmenu[3].SelectFunc = CTFAdmin_MatchSet;
  3353. } else if (ctfgame.match == MATCH_NONE && competition->value) {
  3354. adminmenu[3].text = "Switch to match mode";
  3355. adminmenu[3].SelectFunc = CTFAdmin_MatchMode;
  3356. }
  3357. // if (ent->client->menu)
  3358. // PMenu_Close(ent->client->menu);
  3359. PMenu_Open(ent, adminmenu, -1, sizeof(adminmenu) / sizeof(pmenu_t), NULL);
  3360. }
  3361. void CTFAdmin(edict_t *ent)
  3362. {
  3363. char text[1024];
  3364. if (!allow_admin->value) {
  3365. gi.cprintf(ent, PRINT_HIGH, "Administration is disabled\n");
  3366. return;
  3367. }
  3368. if (gi.argc() > 1 && admin_password->string && *admin_password->string &&
  3369. !ent->client->resp.admin && strcmp(admin_password->string, gi.argv(1)) == 0) {
  3370. ent->client->resp.admin = true;
  3371. gi.bprintf(PRINT_HIGH, "%s has become an admin.\n", ent->client->pers.netname);
  3372. gi.cprintf(ent, PRINT_HIGH, "Type 'admin' to access the adminstration menu.\n");
  3373. }
  3374. if (!ent->client->resp.admin) {
  3375. sprintf(text, "%s has requested admin rights.",
  3376. ent->client->pers.netname);
  3377. CTFBeginElection(ent, ELECT_ADMIN, text);
  3378. return;
  3379. }
  3380. if (ent->client->menu)
  3381. PMenu_Close(ent);
  3382. CTFOpenAdminMenu(ent);
  3383. }
  3384. /*----------------------------------------------------------------*/
  3385. void CTFStats(edict_t *ent)
  3386. {
  3387. int i, e;
  3388. ghost_t *g;
  3389. char st[80];
  3390. char text[1024];
  3391. edict_t *e2;
  3392. *text = 0;
  3393. if (ctfgame.match == MATCH_SETUP) {
  3394. for (i = 1; i <= maxclients->value; i++) {
  3395. e2 = g_edicts + i;
  3396. if (!e2->inuse)
  3397. continue;
  3398. if (!e2->client->resp.ready && e2->client->resp.ctf_team != CTF_NOTEAM) {
  3399. sprintf(st, "%s is not ready.\n", e2->client->pers.netname);
  3400. if (strlen(text) + strlen(st) < sizeof(text) - 50)
  3401. strcat(text, st);
  3402. }
  3403. }
  3404. }
  3405. for (i = 0, g = ctfgame.ghosts; i < MAX_CLIENTS; i++, g++)
  3406. if (g->ent)
  3407. break;
  3408. if (i == MAX_CLIENTS) {
  3409. if (*text)
  3410. gi.cprintf(ent, PRINT_HIGH, "%s", text);
  3411. gi.cprintf(ent, PRINT_HIGH, "No statistics available.\n");
  3412. return;
  3413. }
  3414. strcat(text, " #|Name |Score|Kills|Death|BasDf|CarDf|Effcy|\n");
  3415. for (i = 0, g = ctfgame.ghosts; i < MAX_CLIENTS; i++, g++) {
  3416. if (!*g->netname)
  3417. continue;
  3418. if (g->deaths + g->kills == 0)
  3419. e = 50;
  3420. else
  3421. e = g->kills * 100 / (g->kills + g->deaths);
  3422. sprintf(st, "%3d|%-16.16s|%5d|%5d|%5d|%5d|%5d|%4d%%|\n",
  3423. g->number,
  3424. g->netname,
  3425. g->score,
  3426. g->kills,
  3427. g->deaths,
  3428. g->basedef,
  3429. g->carrierdef,
  3430. e);
  3431. if (strlen(text) + strlen(st) > sizeof(text) - 50) {
  3432. sprintf(text+strlen(text), "And more...\n");
  3433. gi.cprintf(ent, PRINT_HIGH, "%s", text);
  3434. return;
  3435. }
  3436. strcat(text, st);
  3437. }
  3438. gi.cprintf(ent, PRINT_HIGH, "%s", text);
  3439. }
  3440. void CTFPlayerList(edict_t *ent)
  3441. {
  3442. int i;
  3443. char st[80];
  3444. char text[1400];
  3445. edict_t *e2;
  3446. #if 0
  3447. *text = 0;
  3448. if (ctfgame.match == MATCH_SETUP) {
  3449. for (i = 1; i <= maxclients->value; i++) {
  3450. e2 = g_edicts + i;
  3451. if (!e2->inuse)
  3452. continue;
  3453. if (!e2->client->resp.ready && e2->client->resp.ctf_team != CTF_NOTEAM) {
  3454. sprintf(st, "%s is not ready.\n", e2->client->pers.netname);
  3455. if (strlen(text) + strlen(st) < sizeof(text) - 50)
  3456. strcat(text, st);
  3457. }
  3458. }
  3459. }
  3460. #endif
  3461. // number, name, connect time, ping, score, admin
  3462. *text = 0;
  3463. for (i = 1; i <= maxclients->value; i++) {
  3464. e2 = g_edicts + i;
  3465. if (!e2->inuse)
  3466. continue;
  3467. Com_sprintf(st, sizeof(st), "%3d %-16.16s %02d:%02d %4d %3d%s%s\n",
  3468. i,
  3469. e2->client->pers.netname,
  3470. (level.framenum - e2->client->resp.enterframe) / 600,
  3471. ((level.framenum - e2->client->resp.enterframe) % 600)/10,
  3472. e2->client->ping,
  3473. e2->client->resp.score,
  3474. (ctfgame.match == MATCH_SETUP || ctfgame.match == MATCH_PREGAME) ?
  3475. (e2->client->resp.ready ? " (ready)" : " (notready)") : "",
  3476. e2->client->resp.admin ? " (admin)" : "");
  3477. if (strlen(text) + strlen(st) > sizeof(text) - 50) {
  3478. sprintf(text+strlen(text), "And more...\n");
  3479. gi.cprintf(ent, PRINT_HIGH, "%s", text);
  3480. return;
  3481. }
  3482. strcat(text, st);
  3483. }
  3484. gi.cprintf(ent, PRINT_HIGH, "%s", text);
  3485. }
  3486. void CTFWarp(edict_t *ent)
  3487. {
  3488. char text[1024];
  3489. char *mlist, *token;
  3490. static const char *seps = " \t\n\r";
  3491. if (gi.argc() < 2) {
  3492. gi.cprintf(ent, PRINT_HIGH, "Where do you want to warp to?\n");
  3493. gi.cprintf(ent, PRINT_HIGH, "Available levels are: %s\n", warp_list->string);
  3494. return;
  3495. }
  3496. mlist = strdup(warp_list->string);
  3497. token = strtok(mlist, seps);
  3498. while (token != NULL) {
  3499. if (Q_stricmp(token, gi.argv(1)) == 0)
  3500. break;
  3501. token = strtok(NULL, seps);
  3502. }
  3503. if (token == NULL) {
  3504. gi.cprintf(ent, PRINT_HIGH, "Unknown CTF level.\n");
  3505. gi.cprintf(ent, PRINT_HIGH, "Available levels are: %s\n", warp_list->string);
  3506. free(mlist);
  3507. return;
  3508. }
  3509. free(mlist);
  3510. if (ent->client->resp.admin) {
  3511. gi.bprintf(PRINT_HIGH, "%s is warping to level %s.\n",
  3512. ent->client->pers.netname, gi.argv(1));
  3513. strncpy(level.forcemap, gi.argv(1), sizeof(level.forcemap) - 1);
  3514. EndDMLevel();
  3515. return;
  3516. }
  3517. sprintf(text, "%s has requested warping to level %s.",
  3518. ent->client->pers.netname, gi.argv(1));
  3519. if (CTFBeginElection(ent, ELECT_MAP, text))
  3520. strncpy(ctfgame.elevel, gi.argv(1), sizeof(ctfgame.elevel) - 1);
  3521. }
  3522. void CTFBoot(edict_t *ent)
  3523. {
  3524. int i;
  3525. edict_t *targ;
  3526. char text[80];
  3527. if (!ent->client->resp.admin) {
  3528. gi.cprintf(ent, PRINT_HIGH, "You are not an admin.\n");
  3529. return;
  3530. }
  3531. if (gi.argc() < 2) {
  3532. gi.cprintf(ent, PRINT_HIGH, "Who do you want to kick?\n");
  3533. return;
  3534. }
  3535. if (*gi.argv(1) < '0' && *gi.argv(1) > '9') {
  3536. gi.cprintf(ent, PRINT_HIGH, "Specify the player number to kick.\n");
  3537. return;
  3538. }
  3539. i = atoi(gi.argv(1));
  3540. if (i < 1 || i > maxclients->value) {
  3541. gi.cprintf(ent, PRINT_HIGH, "Invalid player number.\n");
  3542. return;
  3543. }
  3544. targ = g_edicts + i;
  3545. if (!targ->inuse) {
  3546. gi.cprintf(ent, PRINT_HIGH, "That player number is not connected.\n");
  3547. return;
  3548. }
  3549. sprintf(text, "kick %d\n", i - 1);
  3550. gi.AddCommandString(text);
  3551. }
  3552. void CTFSetPowerUpEffect(edict_t *ent, int def)
  3553. {
  3554. if (ent->client->resp.ctf_team == CTF_TEAM1)
  3555. ent->s.effects |= EF_PENT; // red
  3556. else if (ent->client->resp.ctf_team == CTF_TEAM2)
  3557. ent->s.effects |= EF_QUAD; // red
  3558. else
  3559. ent->s.effects |= def;
  3560. }