g_team.c 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484
  1. /*
  2. ===========================================================================
  3. Copyright (C) 1999-2005 Id Software, Inc.
  4. This file is part of Quake III Arena source code.
  5. Quake III Arena source code is free software; you can redistribute it
  6. and/or modify it under the terms of the GNU General Public License as
  7. published by the Free Software Foundation; either version 2 of the License,
  8. or (at your option) any later version.
  9. Quake III Arena source code is distributed in the hope that it will be
  10. useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with Foobar; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. ===========================================================================
  17. */
  18. //
  19. #include "g_local.h"
  20. typedef struct teamgame_s {
  21. float last_flag_capture;
  22. int last_capture_team;
  23. flagStatus_t redStatus; // CTF
  24. flagStatus_t blueStatus; // CTF
  25. flagStatus_t flagStatus; // One Flag CTF
  26. int redTakenTime;
  27. int blueTakenTime;
  28. int redObeliskAttackedTime;
  29. int blueObeliskAttackedTime;
  30. } teamgame_t;
  31. teamgame_t teamgame;
  32. gentity_t *neutralObelisk;
  33. void Team_SetFlagStatus( int team, flagStatus_t status );
  34. void Team_InitGame( void ) {
  35. memset(&teamgame, 0, sizeof teamgame);
  36. switch( g_gametype.integer ) {
  37. case GT_CTF:
  38. teamgame.redStatus = teamgame.blueStatus = -1; // Invalid to force update
  39. Team_SetFlagStatus( TEAM_RED, FLAG_ATBASE );
  40. Team_SetFlagStatus( TEAM_BLUE, FLAG_ATBASE );
  41. break;
  42. #ifdef MISSIONPACK
  43. case GT_1FCTF:
  44. teamgame.flagStatus = -1; // Invalid to force update
  45. Team_SetFlagStatus( TEAM_FREE, FLAG_ATBASE );
  46. break;
  47. #endif
  48. default:
  49. break;
  50. }
  51. }
  52. int OtherTeam(int team) {
  53. if (team==TEAM_RED)
  54. return TEAM_BLUE;
  55. else if (team==TEAM_BLUE)
  56. return TEAM_RED;
  57. return team;
  58. }
  59. const char *TeamName(int team) {
  60. if (team==TEAM_RED)
  61. return "RED";
  62. else if (team==TEAM_BLUE)
  63. return "BLUE";
  64. else if (team==TEAM_SPECTATOR)
  65. return "SPECTATOR";
  66. return "FREE";
  67. }
  68. const char *OtherTeamName(int team) {
  69. if (team==TEAM_RED)
  70. return "BLUE";
  71. else if (team==TEAM_BLUE)
  72. return "RED";
  73. else if (team==TEAM_SPECTATOR)
  74. return "SPECTATOR";
  75. return "FREE";
  76. }
  77. const char *TeamColorString(int team) {
  78. if (team==TEAM_RED)
  79. return S_COLOR_RED;
  80. else if (team==TEAM_BLUE)
  81. return S_COLOR_BLUE;
  82. else if (team==TEAM_SPECTATOR)
  83. return S_COLOR_YELLOW;
  84. return S_COLOR_WHITE;
  85. }
  86. // NULL for everyone
  87. void QDECL PrintMsg( gentity_t *ent, const char *fmt, ... ) {
  88. char msg[1024];
  89. va_list argptr;
  90. char *p;
  91. va_start (argptr,fmt);
  92. if (vsprintf (msg, fmt, argptr) > sizeof(msg)) {
  93. G_Error ( "PrintMsg overrun" );
  94. }
  95. va_end (argptr);
  96. // double quotes are bad
  97. while ((p = strchr(msg, '"')) != NULL)
  98. *p = '\'';
  99. trap_SendServerCommand ( ( (ent == NULL) ? -1 : ent-g_entities ), va("print \"%s\"", msg ));
  100. }
  101. /*
  102. ==============
  103. AddTeamScore
  104. used for gametype > GT_TEAM
  105. for gametype GT_TEAM the level.teamScores is updated in AddScore in g_combat.c
  106. ==============
  107. */
  108. void AddTeamScore(vec3_t origin, int team, int score) {
  109. gentity_t *te;
  110. te = G_TempEntity(origin, EV_GLOBAL_TEAM_SOUND );
  111. te->r.svFlags |= SVF_BROADCAST;
  112. if ( team == TEAM_RED ) {
  113. if ( level.teamScores[ TEAM_RED ] + score == level.teamScores[ TEAM_BLUE ] ) {
  114. //teams are tied sound
  115. te->s.eventParm = GTS_TEAMS_ARE_TIED;
  116. }
  117. else if ( level.teamScores[ TEAM_RED ] <= level.teamScores[ TEAM_BLUE ] &&
  118. level.teamScores[ TEAM_RED ] + score > level.teamScores[ TEAM_BLUE ]) {
  119. // red took the lead sound
  120. te->s.eventParm = GTS_REDTEAM_TOOK_LEAD;
  121. }
  122. else {
  123. // red scored sound
  124. te->s.eventParm = GTS_REDTEAM_SCORED;
  125. }
  126. }
  127. else {
  128. if ( level.teamScores[ TEAM_BLUE ] + score == level.teamScores[ TEAM_RED ] ) {
  129. //teams are tied sound
  130. te->s.eventParm = GTS_TEAMS_ARE_TIED;
  131. }
  132. else if ( level.teamScores[ TEAM_BLUE ] <= level.teamScores[ TEAM_RED ] &&
  133. level.teamScores[ TEAM_BLUE ] + score > level.teamScores[ TEAM_RED ]) {
  134. // blue took the lead sound
  135. te->s.eventParm = GTS_BLUETEAM_TOOK_LEAD;
  136. }
  137. else {
  138. // blue scored sound
  139. te->s.eventParm = GTS_BLUETEAM_SCORED;
  140. }
  141. }
  142. level.teamScores[ team ] += score;
  143. }
  144. /*
  145. ==============
  146. OnSameTeam
  147. ==============
  148. */
  149. qboolean OnSameTeam( gentity_t *ent1, gentity_t *ent2 ) {
  150. if ( !ent1->client || !ent2->client ) {
  151. return qfalse;
  152. }
  153. if ( g_gametype.integer < GT_TEAM ) {
  154. return qfalse;
  155. }
  156. if ( ent1->client->sess.sessionTeam == ent2->client->sess.sessionTeam ) {
  157. return qtrue;
  158. }
  159. return qfalse;
  160. }
  161. static char ctfFlagStatusRemap[] = { '0', '1', '*', '*', '2' };
  162. static char oneFlagStatusRemap[] = { '0', '1', '2', '3', '4' };
  163. void Team_SetFlagStatus( int team, flagStatus_t status ) {
  164. qboolean modified = qfalse;
  165. switch( team ) {
  166. case TEAM_RED: // CTF
  167. if( teamgame.redStatus != status ) {
  168. teamgame.redStatus = status;
  169. modified = qtrue;
  170. }
  171. break;
  172. case TEAM_BLUE: // CTF
  173. if( teamgame.blueStatus != status ) {
  174. teamgame.blueStatus = status;
  175. modified = qtrue;
  176. }
  177. break;
  178. case TEAM_FREE: // One Flag CTF
  179. if( teamgame.flagStatus != status ) {
  180. teamgame.flagStatus = status;
  181. modified = qtrue;
  182. }
  183. break;
  184. }
  185. if( modified ) {
  186. char st[4];
  187. if( g_gametype.integer == GT_CTF ) {
  188. st[0] = ctfFlagStatusRemap[teamgame.redStatus];
  189. st[1] = ctfFlagStatusRemap[teamgame.blueStatus];
  190. st[2] = 0;
  191. }
  192. else { // GT_1FCTF
  193. st[0] = oneFlagStatusRemap[teamgame.flagStatus];
  194. st[1] = 0;
  195. }
  196. trap_SetConfigstring( CS_FLAGSTATUS, st );
  197. }
  198. }
  199. void Team_CheckDroppedItem( gentity_t *dropped ) {
  200. if( dropped->item->giTag == PW_REDFLAG ) {
  201. Team_SetFlagStatus( TEAM_RED, FLAG_DROPPED );
  202. }
  203. else if( dropped->item->giTag == PW_BLUEFLAG ) {
  204. Team_SetFlagStatus( TEAM_BLUE, FLAG_DROPPED );
  205. }
  206. else if( dropped->item->giTag == PW_NEUTRALFLAG ) {
  207. Team_SetFlagStatus( TEAM_FREE, FLAG_DROPPED );
  208. }
  209. }
  210. /*
  211. ================
  212. Team_ForceGesture
  213. ================
  214. */
  215. void Team_ForceGesture(int team) {
  216. int i;
  217. gentity_t *ent;
  218. for (i = 0; i < MAX_CLIENTS; i++) {
  219. ent = &g_entities[i];
  220. if (!ent->inuse)
  221. continue;
  222. if (!ent->client)
  223. continue;
  224. if (ent->client->sess.sessionTeam != team)
  225. continue;
  226. //
  227. ent->flags |= FL_FORCE_GESTURE;
  228. }
  229. }
  230. /*
  231. ================
  232. Team_FragBonuses
  233. Calculate the bonuses for flag defense, flag carrier defense, etc.
  234. Note that bonuses are not cumulative. You get one, they are in importance
  235. order.
  236. ================
  237. */
  238. void Team_FragBonuses(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker)
  239. {
  240. int i;
  241. gentity_t *ent;
  242. int flag_pw, enemy_flag_pw;
  243. int otherteam;
  244. int tokens;
  245. gentity_t *flag, *carrier = NULL;
  246. char *c;
  247. vec3_t v1, v2;
  248. int team;
  249. // no bonus for fragging yourself or team mates
  250. if (!targ->client || !attacker->client || targ == attacker || OnSameTeam(targ, attacker))
  251. return;
  252. team = targ->client->sess.sessionTeam;
  253. otherteam = OtherTeam(targ->client->sess.sessionTeam);
  254. if (otherteam < 0)
  255. return; // whoever died isn't on a team
  256. // same team, if the flag at base, check to he has the enemy flag
  257. if (team == TEAM_RED) {
  258. flag_pw = PW_REDFLAG;
  259. enemy_flag_pw = PW_BLUEFLAG;
  260. } else {
  261. flag_pw = PW_BLUEFLAG;
  262. enemy_flag_pw = PW_REDFLAG;
  263. }
  264. if (g_gametype.integer == GT_1FCTF) {
  265. enemy_flag_pw = PW_NEUTRALFLAG;
  266. }
  267. // did the attacker frag the flag carrier?
  268. tokens = 0;
  269. #ifdef MISSIONPACK
  270. if( g_gametype.integer == GT_HARVESTER ) {
  271. tokens = targ->client->ps.generic1;
  272. }
  273. #endif
  274. if (targ->client->ps.powerups[enemy_flag_pw]) {
  275. attacker->client->pers.teamState.lastfraggedcarrier = level.time;
  276. AddScore(attacker, targ->r.currentOrigin, CTF_FRAG_CARRIER_BONUS);
  277. attacker->client->pers.teamState.fragcarrier++;
  278. PrintMsg(NULL, "%s" S_COLOR_WHITE " fragged %s's flag carrier!\n",
  279. attacker->client->pers.netname, TeamName(team));
  280. // the target had the flag, clear the hurt carrier
  281. // field on the other team
  282. for (i = 0; i < g_maxclients.integer; i++) {
  283. ent = g_entities + i;
  284. if (ent->inuse && ent->client->sess.sessionTeam == otherteam)
  285. ent->client->pers.teamState.lasthurtcarrier = 0;
  286. }
  287. return;
  288. }
  289. // did the attacker frag a head carrier? other->client->ps.generic1
  290. if (tokens) {
  291. attacker->client->pers.teamState.lastfraggedcarrier = level.time;
  292. AddScore(attacker, targ->r.currentOrigin, CTF_FRAG_CARRIER_BONUS * tokens * tokens);
  293. attacker->client->pers.teamState.fragcarrier++;
  294. PrintMsg(NULL, "%s" S_COLOR_WHITE " fragged %s's skull carrier!\n",
  295. attacker->client->pers.netname, TeamName(team));
  296. // the target had the flag, clear the hurt carrier
  297. // field on the other team
  298. for (i = 0; i < g_maxclients.integer; i++) {
  299. ent = g_entities + i;
  300. if (ent->inuse && ent->client->sess.sessionTeam == otherteam)
  301. ent->client->pers.teamState.lasthurtcarrier = 0;
  302. }
  303. return;
  304. }
  305. if (targ->client->pers.teamState.lasthurtcarrier &&
  306. level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT &&
  307. !attacker->client->ps.powerups[flag_pw]) {
  308. // attacker is on the same team as the flag carrier and
  309. // fragged a guy who hurt our flag carrier
  310. AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_DANGER_PROTECT_BONUS);
  311. attacker->client->pers.teamState.carrierdefense++;
  312. targ->client->pers.teamState.lasthurtcarrier = 0;
  313. attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
  314. team = attacker->client->sess.sessionTeam;
  315. // add the sprite over the player's head
  316. attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
  317. attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
  318. attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
  319. return;
  320. }
  321. if (targ->client->pers.teamState.lasthurtcarrier &&
  322. level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT) {
  323. // attacker is on the same team as the skull carrier and
  324. AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_DANGER_PROTECT_BONUS);
  325. attacker->client->pers.teamState.carrierdefense++;
  326. targ->client->pers.teamState.lasthurtcarrier = 0;
  327. attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
  328. team = attacker->client->sess.sessionTeam;
  329. // add the sprite over the player's head
  330. attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
  331. attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
  332. attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
  333. return;
  334. }
  335. // flag and flag carrier area defense bonuses
  336. // we have to find the flag and carrier entities
  337. #ifdef MISSIONPACK
  338. if( g_gametype.integer == GT_OBELISK ) {
  339. // find the team obelisk
  340. switch (attacker->client->sess.sessionTeam) {
  341. case TEAM_RED:
  342. c = "team_redobelisk";
  343. break;
  344. case TEAM_BLUE:
  345. c = "team_blueobelisk";
  346. break;
  347. default:
  348. return;
  349. }
  350. } else if (g_gametype.integer == GT_HARVESTER ) {
  351. // find the center obelisk
  352. c = "team_neutralobelisk";
  353. } else {
  354. #endif
  355. // find the flag
  356. switch (attacker->client->sess.sessionTeam) {
  357. case TEAM_RED:
  358. c = "team_CTF_redflag";
  359. break;
  360. case TEAM_BLUE:
  361. c = "team_CTF_blueflag";
  362. break;
  363. default:
  364. return;
  365. }
  366. // find attacker's team's flag carrier
  367. for (i = 0; i < g_maxclients.integer; i++) {
  368. carrier = g_entities + i;
  369. if (carrier->inuse && carrier->client->ps.powerups[flag_pw])
  370. break;
  371. carrier = NULL;
  372. }
  373. #ifdef MISSIONPACK
  374. }
  375. #endif
  376. flag = NULL;
  377. while ((flag = G_Find (flag, FOFS(classname), c)) != NULL) {
  378. if (!(flag->flags & FL_DROPPED_ITEM))
  379. break;
  380. }
  381. if (!flag)
  382. return; // can't find attacker's flag
  383. // ok we have the attackers flag and a pointer to the carrier
  384. // check to see if we are defending the base's flag
  385. VectorSubtract(targ->r.currentOrigin, flag->r.currentOrigin, v1);
  386. VectorSubtract(attacker->r.currentOrigin, flag->r.currentOrigin, v2);
  387. if ( ( ( VectorLength(v1) < CTF_TARGET_PROTECT_RADIUS &&
  388. trap_InPVS(flag->r.currentOrigin, targ->r.currentOrigin ) ) ||
  389. ( VectorLength(v2) < CTF_TARGET_PROTECT_RADIUS &&
  390. trap_InPVS(flag->r.currentOrigin, attacker->r.currentOrigin ) ) ) &&
  391. attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) {
  392. // we defended the base flag
  393. AddScore(attacker, targ->r.currentOrigin, CTF_FLAG_DEFENSE_BONUS);
  394. attacker->client->pers.teamState.basedefense++;
  395. attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
  396. // add the sprite over the player's head
  397. attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
  398. attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
  399. attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
  400. return;
  401. }
  402. if (carrier && carrier != attacker) {
  403. VectorSubtract(targ->r.currentOrigin, carrier->r.currentOrigin, v1);
  404. VectorSubtract(attacker->r.currentOrigin, carrier->r.currentOrigin, v1);
  405. if ( ( ( VectorLength(v1) < CTF_ATTACKER_PROTECT_RADIUS &&
  406. trap_InPVS(carrier->r.currentOrigin, targ->r.currentOrigin ) ) ||
  407. ( VectorLength(v2) < CTF_ATTACKER_PROTECT_RADIUS &&
  408. trap_InPVS(carrier->r.currentOrigin, attacker->r.currentOrigin ) ) ) &&
  409. attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) {
  410. AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_PROTECT_BONUS);
  411. attacker->client->pers.teamState.carrierdefense++;
  412. attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
  413. // add the sprite over the player's head
  414. attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
  415. attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
  416. attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
  417. return;
  418. }
  419. }
  420. }
  421. /*
  422. ================
  423. Team_CheckHurtCarrier
  424. Check to see if attacker hurt the flag carrier. Needed when handing out bonuses for assistance to flag
  425. carrier defense.
  426. ================
  427. */
  428. void Team_CheckHurtCarrier(gentity_t *targ, gentity_t *attacker)
  429. {
  430. int flag_pw;
  431. if (!targ->client || !attacker->client)
  432. return;
  433. if (targ->client->sess.sessionTeam == TEAM_RED)
  434. flag_pw = PW_BLUEFLAG;
  435. else
  436. flag_pw = PW_REDFLAG;
  437. // flags
  438. if (targ->client->ps.powerups[flag_pw] &&
  439. targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam)
  440. attacker->client->pers.teamState.lasthurtcarrier = level.time;
  441. // skulls
  442. if (targ->client->ps.generic1 &&
  443. targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam)
  444. attacker->client->pers.teamState.lasthurtcarrier = level.time;
  445. }
  446. gentity_t *Team_ResetFlag( int team ) {
  447. char *c;
  448. gentity_t *ent, *rent = NULL;
  449. switch (team) {
  450. case TEAM_RED:
  451. c = "team_CTF_redflag";
  452. break;
  453. case TEAM_BLUE:
  454. c = "team_CTF_blueflag";
  455. break;
  456. case TEAM_FREE:
  457. c = "team_CTF_neutralflag";
  458. break;
  459. default:
  460. return NULL;
  461. }
  462. ent = NULL;
  463. while ((ent = G_Find (ent, FOFS(classname), c)) != NULL) {
  464. if (ent->flags & FL_DROPPED_ITEM)
  465. G_FreeEntity(ent);
  466. else {
  467. rent = ent;
  468. RespawnItem(ent);
  469. }
  470. }
  471. Team_SetFlagStatus( team, FLAG_ATBASE );
  472. return rent;
  473. }
  474. void Team_ResetFlags( void ) {
  475. if( g_gametype.integer == GT_CTF ) {
  476. Team_ResetFlag( TEAM_RED );
  477. Team_ResetFlag( TEAM_BLUE );
  478. }
  479. #ifdef MISSIONPACK
  480. else if( g_gametype.integer == GT_1FCTF ) {
  481. Team_ResetFlag( TEAM_FREE );
  482. }
  483. #endif
  484. }
  485. void Team_ReturnFlagSound( gentity_t *ent, int team ) {
  486. gentity_t *te;
  487. if (ent == NULL) {
  488. G_Printf ("Warning: NULL passed to Team_ReturnFlagSound\n");
  489. return;
  490. }
  491. te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_TEAM_SOUND );
  492. if( team == TEAM_BLUE ) {
  493. te->s.eventParm = GTS_RED_RETURN;
  494. }
  495. else {
  496. te->s.eventParm = GTS_BLUE_RETURN;
  497. }
  498. te->r.svFlags |= SVF_BROADCAST;
  499. }
  500. void Team_TakeFlagSound( gentity_t *ent, int team ) {
  501. gentity_t *te;
  502. if (ent == NULL) {
  503. G_Printf ("Warning: NULL passed to Team_TakeFlagSound\n");
  504. return;
  505. }
  506. // only play sound when the flag was at the base
  507. // or not picked up the last 10 seconds
  508. switch(team) {
  509. case TEAM_RED:
  510. if( teamgame.blueStatus != FLAG_ATBASE ) {
  511. if (teamgame.blueTakenTime > level.time - 10000)
  512. return;
  513. }
  514. teamgame.blueTakenTime = level.time;
  515. break;
  516. case TEAM_BLUE: // CTF
  517. if( teamgame.redStatus != FLAG_ATBASE ) {
  518. if (teamgame.redTakenTime > level.time - 10000)
  519. return;
  520. }
  521. teamgame.redTakenTime = level.time;
  522. break;
  523. }
  524. te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_TEAM_SOUND );
  525. if( team == TEAM_BLUE ) {
  526. te->s.eventParm = GTS_RED_TAKEN;
  527. }
  528. else {
  529. te->s.eventParm = GTS_BLUE_TAKEN;
  530. }
  531. te->r.svFlags |= SVF_BROADCAST;
  532. }
  533. void Team_CaptureFlagSound( gentity_t *ent, int team ) {
  534. gentity_t *te;
  535. if (ent == NULL) {
  536. G_Printf ("Warning: NULL passed to Team_CaptureFlagSound\n");
  537. return;
  538. }
  539. te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_TEAM_SOUND );
  540. if( team == TEAM_BLUE ) {
  541. te->s.eventParm = GTS_BLUE_CAPTURE;
  542. }
  543. else {
  544. te->s.eventParm = GTS_RED_CAPTURE;
  545. }
  546. te->r.svFlags |= SVF_BROADCAST;
  547. }
  548. void Team_ReturnFlag( int team ) {
  549. Team_ReturnFlagSound(Team_ResetFlag(team), team);
  550. if( team == TEAM_FREE ) {
  551. PrintMsg(NULL, "The flag has returned!\n" );
  552. }
  553. else {
  554. PrintMsg(NULL, "The %s flag has returned!\n", TeamName(team));
  555. }
  556. }
  557. void Team_FreeEntity( gentity_t *ent ) {
  558. if( ent->item->giTag == PW_REDFLAG ) {
  559. Team_ReturnFlag( TEAM_RED );
  560. }
  561. else if( ent->item->giTag == PW_BLUEFLAG ) {
  562. Team_ReturnFlag( TEAM_BLUE );
  563. }
  564. else if( ent->item->giTag == PW_NEUTRALFLAG ) {
  565. Team_ReturnFlag( TEAM_FREE );
  566. }
  567. }
  568. /*
  569. ==============
  570. Team_DroppedFlagThink
  571. Automatically set in Launch_Item if the item is one of the flags
  572. Flags are unique in that if they are dropped, the base flag must be respawned when they time out
  573. ==============
  574. */
  575. void Team_DroppedFlagThink(gentity_t *ent) {
  576. int team = TEAM_FREE;
  577. if( ent->item->giTag == PW_REDFLAG ) {
  578. team = TEAM_RED;
  579. }
  580. else if( ent->item->giTag == PW_BLUEFLAG ) {
  581. team = TEAM_BLUE;
  582. }
  583. else if( ent->item->giTag == PW_NEUTRALFLAG ) {
  584. team = TEAM_FREE;
  585. }
  586. Team_ReturnFlagSound( Team_ResetFlag( team ), team );
  587. // Reset Flag will delete this entity
  588. }
  589. /*
  590. ==============
  591. Team_DroppedFlagThink
  592. ==============
  593. */
  594. int Team_TouchOurFlag( gentity_t *ent, gentity_t *other, int team ) {
  595. int i;
  596. gentity_t *player;
  597. gclient_t *cl = other->client;
  598. int enemy_flag;
  599. #ifdef MISSIONPACK
  600. if( g_gametype.integer == GT_1FCTF ) {
  601. enemy_flag = PW_NEUTRALFLAG;
  602. }
  603. else {
  604. #endif
  605. if (cl->sess.sessionTeam == TEAM_RED) {
  606. enemy_flag = PW_BLUEFLAG;
  607. } else {
  608. enemy_flag = PW_REDFLAG;
  609. }
  610. if ( ent->flags & FL_DROPPED_ITEM ) {
  611. // hey, its not home. return it by teleporting it back
  612. PrintMsg( NULL, "%s" S_COLOR_WHITE " returned the %s flag!\n",
  613. cl->pers.netname, TeamName(team));
  614. AddScore(other, ent->r.currentOrigin, CTF_RECOVERY_BONUS);
  615. other->client->pers.teamState.flagrecovery++;
  616. other->client->pers.teamState.lastreturnedflag = level.time;
  617. //ResetFlag will remove this entity! We must return zero
  618. Team_ReturnFlagSound(Team_ResetFlag(team), team);
  619. return 0;
  620. }
  621. #ifdef MISSIONPACK
  622. }
  623. #endif
  624. // the flag is at home base. if the player has the enemy
  625. // flag, he's just won!
  626. if (!cl->ps.powerups[enemy_flag])
  627. return 0; // We don't have the flag
  628. #ifdef MISSIONPACK
  629. if( g_gametype.integer == GT_1FCTF ) {
  630. PrintMsg( NULL, "%s" S_COLOR_WHITE " captured the flag!\n", cl->pers.netname );
  631. }
  632. else {
  633. #endif
  634. PrintMsg( NULL, "%s" S_COLOR_WHITE " captured the %s flag!\n", cl->pers.netname, TeamName(OtherTeam(team)));
  635. #ifdef MISSIONPACK
  636. }
  637. #endif
  638. cl->ps.powerups[enemy_flag] = 0;
  639. teamgame.last_flag_capture = level.time;
  640. teamgame.last_capture_team = team;
  641. // Increase the team's score
  642. AddTeamScore(ent->s.pos.trBase, other->client->sess.sessionTeam, 1);
  643. Team_ForceGesture(other->client->sess.sessionTeam);
  644. other->client->pers.teamState.captures++;
  645. // add the sprite over the player's head
  646. other->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
  647. other->client->ps.eFlags |= EF_AWARD_CAP;
  648. other->client->rewardTime = level.time + REWARD_SPRITE_TIME;
  649. other->client->ps.persistant[PERS_CAPTURES]++;
  650. // other gets another 10 frag bonus
  651. AddScore(other, ent->r.currentOrigin, CTF_CAPTURE_BONUS);
  652. Team_CaptureFlagSound( ent, team );
  653. // Ok, let's do the player loop, hand out the bonuses
  654. for (i = 0; i < g_maxclients.integer; i++) {
  655. player = &g_entities[i];
  656. if (!player->inuse)
  657. continue;
  658. if (player->client->sess.sessionTeam !=
  659. cl->sess.sessionTeam) {
  660. player->client->pers.teamState.lasthurtcarrier = -5;
  661. } else if (player->client->sess.sessionTeam ==
  662. cl->sess.sessionTeam) {
  663. if (player != other)
  664. AddScore(player, ent->r.currentOrigin, CTF_TEAM_BONUS);
  665. // award extra points for capture assists
  666. if (player->client->pers.teamState.lastreturnedflag +
  667. CTF_RETURN_FLAG_ASSIST_TIMEOUT > level.time) {
  668. AddScore (player, ent->r.currentOrigin, CTF_RETURN_FLAG_ASSIST_BONUS);
  669. other->client->pers.teamState.assists++;
  670. player->client->ps.persistant[PERS_ASSIST_COUNT]++;
  671. // add the sprite over the player's head
  672. player->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
  673. player->client->ps.eFlags |= EF_AWARD_ASSIST;
  674. player->client->rewardTime = level.time + REWARD_SPRITE_TIME;
  675. } else if (player->client->pers.teamState.lastfraggedcarrier +
  676. CTF_FRAG_CARRIER_ASSIST_TIMEOUT > level.time) {
  677. AddScore(player, ent->r.currentOrigin, CTF_FRAG_CARRIER_ASSIST_BONUS);
  678. other->client->pers.teamState.assists++;
  679. player->client->ps.persistant[PERS_ASSIST_COUNT]++;
  680. // add the sprite over the player's head
  681. player->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
  682. player->client->ps.eFlags |= EF_AWARD_ASSIST;
  683. player->client->rewardTime = level.time + REWARD_SPRITE_TIME;
  684. }
  685. }
  686. }
  687. Team_ResetFlags();
  688. CalculateRanks();
  689. return 0; // Do not respawn this automatically
  690. }
  691. int Team_TouchEnemyFlag( gentity_t *ent, gentity_t *other, int team ) {
  692. gclient_t *cl = other->client;
  693. #ifdef MISSIONPACK
  694. if( g_gametype.integer == GT_1FCTF ) {
  695. PrintMsg (NULL, "%s" S_COLOR_WHITE " got the flag!\n", other->client->pers.netname );
  696. cl->ps.powerups[PW_NEUTRALFLAG] = INT_MAX; // flags never expire
  697. if( team == TEAM_RED ) {
  698. Team_SetFlagStatus( TEAM_FREE, FLAG_TAKEN_RED );
  699. }
  700. else {
  701. Team_SetFlagStatus( TEAM_FREE, FLAG_TAKEN_BLUE );
  702. }
  703. }
  704. else{
  705. #endif
  706. PrintMsg (NULL, "%s" S_COLOR_WHITE " got the %s flag!\n",
  707. other->client->pers.netname, TeamName(team));
  708. if (team == TEAM_RED)
  709. cl->ps.powerups[PW_REDFLAG] = INT_MAX; // flags never expire
  710. else
  711. cl->ps.powerups[PW_BLUEFLAG] = INT_MAX; // flags never expire
  712. Team_SetFlagStatus( team, FLAG_TAKEN );
  713. #ifdef MISSIONPACK
  714. }
  715. #endif
  716. AddScore(other, ent->r.currentOrigin, CTF_FLAG_BONUS);
  717. cl->pers.teamState.flagsince = level.time;
  718. Team_TakeFlagSound( ent, team );
  719. return -1; // Do not respawn this automatically, but do delete it if it was FL_DROPPED
  720. }
  721. int Pickup_Team( gentity_t *ent, gentity_t *other ) {
  722. int team;
  723. gclient_t *cl = other->client;
  724. #ifdef MISSIONPACK
  725. if( g_gametype.integer == GT_OBELISK ) {
  726. // there are no team items that can be picked up in obelisk
  727. G_FreeEntity( ent );
  728. return 0;
  729. }
  730. if( g_gametype.integer == GT_HARVESTER ) {
  731. // the only team items that can be picked up in harvester are the cubes
  732. if( ent->spawnflags != cl->sess.sessionTeam ) {
  733. cl->ps.generic1 += 1;
  734. }
  735. G_FreeEntity( ent );
  736. return 0;
  737. }
  738. #endif
  739. // figure out what team this flag is
  740. if( strcmp(ent->classname, "team_CTF_redflag") == 0 ) {
  741. team = TEAM_RED;
  742. }
  743. else if( strcmp(ent->classname, "team_CTF_blueflag") == 0 ) {
  744. team = TEAM_BLUE;
  745. }
  746. #ifdef MISSIONPACK
  747. else if( strcmp(ent->classname, "team_CTF_neutralflag") == 0 ) {
  748. team = TEAM_FREE;
  749. }
  750. #endif
  751. else {
  752. PrintMsg ( other, "Don't know what team the flag is on.\n");
  753. return 0;
  754. }
  755. #ifdef MISSIONPACK
  756. if( g_gametype.integer == GT_1FCTF ) {
  757. if( team == TEAM_FREE ) {
  758. return Team_TouchEnemyFlag( ent, other, cl->sess.sessionTeam );
  759. }
  760. if( team != cl->sess.sessionTeam) {
  761. return Team_TouchOurFlag( ent, other, cl->sess.sessionTeam );
  762. }
  763. return 0;
  764. }
  765. #endif
  766. // GT_CTF
  767. if( team == cl->sess.sessionTeam) {
  768. return Team_TouchOurFlag( ent, other, team );
  769. }
  770. return Team_TouchEnemyFlag( ent, other, team );
  771. }
  772. /*
  773. ===========
  774. Team_GetLocation
  775. Report a location for the player. Uses placed nearby target_location entities
  776. ============
  777. */
  778. gentity_t *Team_GetLocation(gentity_t *ent)
  779. {
  780. gentity_t *eloc, *best;
  781. float bestlen, len;
  782. vec3_t origin;
  783. best = NULL;
  784. bestlen = 3*8192.0*8192.0;
  785. VectorCopy( ent->r.currentOrigin, origin );
  786. for (eloc = level.locationHead; eloc; eloc = eloc->nextTrain) {
  787. len = ( origin[0] - eloc->r.currentOrigin[0] ) * ( origin[0] - eloc->r.currentOrigin[0] )
  788. + ( origin[1] - eloc->r.currentOrigin[1] ) * ( origin[1] - eloc->r.currentOrigin[1] )
  789. + ( origin[2] - eloc->r.currentOrigin[2] ) * ( origin[2] - eloc->r.currentOrigin[2] );
  790. if ( len > bestlen ) {
  791. continue;
  792. }
  793. if ( !trap_InPVS( origin, eloc->r.currentOrigin ) ) {
  794. continue;
  795. }
  796. bestlen = len;
  797. best = eloc;
  798. }
  799. return best;
  800. }
  801. /*
  802. ===========
  803. Team_GetLocation
  804. Report a location for the player. Uses placed nearby target_location entities
  805. ============
  806. */
  807. qboolean Team_GetLocationMsg(gentity_t *ent, char *loc, int loclen)
  808. {
  809. gentity_t *best;
  810. best = Team_GetLocation( ent );
  811. if (!best)
  812. return qfalse;
  813. if (best->count) {
  814. if (best->count < 0)
  815. best->count = 0;
  816. if (best->count > 7)
  817. best->count = 7;
  818. Com_sprintf(loc, loclen, "%c%c%s" S_COLOR_WHITE, Q_COLOR_ESCAPE, best->count + '0', best->message );
  819. } else
  820. Com_sprintf(loc, loclen, "%s", best->message);
  821. return qtrue;
  822. }
  823. /*---------------------------------------------------------------------------*/
  824. /*
  825. ================
  826. SelectRandomDeathmatchSpawnPoint
  827. go to a random point that doesn't telefrag
  828. ================
  829. */
  830. #define MAX_TEAM_SPAWN_POINTS 32
  831. gentity_t *SelectRandomTeamSpawnPoint( int teamstate, team_t team ) {
  832. gentity_t *spot;
  833. int count;
  834. int selection;
  835. gentity_t *spots[MAX_TEAM_SPAWN_POINTS];
  836. char *classname;
  837. if (teamstate == TEAM_BEGIN) {
  838. if (team == TEAM_RED)
  839. classname = "team_CTF_redplayer";
  840. else if (team == TEAM_BLUE)
  841. classname = "team_CTF_blueplayer";
  842. else
  843. return NULL;
  844. } else {
  845. if (team == TEAM_RED)
  846. classname = "team_CTF_redspawn";
  847. else if (team == TEAM_BLUE)
  848. classname = "team_CTF_bluespawn";
  849. else
  850. return NULL;
  851. }
  852. count = 0;
  853. spot = NULL;
  854. while ((spot = G_Find (spot, FOFS(classname), classname)) != NULL) {
  855. if ( SpotWouldTelefrag( spot ) ) {
  856. continue;
  857. }
  858. spots[ count ] = spot;
  859. if (++count == MAX_TEAM_SPAWN_POINTS)
  860. break;
  861. }
  862. if ( !count ) { // no spots that won't telefrag
  863. return G_Find( NULL, FOFS(classname), classname);
  864. }
  865. selection = rand() % count;
  866. return spots[ selection ];
  867. }
  868. /*
  869. ===========
  870. SelectCTFSpawnPoint
  871. ============
  872. */
  873. gentity_t *SelectCTFSpawnPoint ( team_t team, int teamstate, vec3_t origin, vec3_t angles ) {
  874. gentity_t *spot;
  875. spot = SelectRandomTeamSpawnPoint ( teamstate, team );
  876. if (!spot) {
  877. return SelectSpawnPoint( vec3_origin, origin, angles );
  878. }
  879. VectorCopy (spot->s.origin, origin);
  880. origin[2] += 9;
  881. VectorCopy (spot->s.angles, angles);
  882. return spot;
  883. }
  884. /*---------------------------------------------------------------------------*/
  885. static int QDECL SortClients( const void *a, const void *b ) {
  886. return *(int *)a - *(int *)b;
  887. }
  888. /*
  889. ==================
  890. TeamplayLocationsMessage
  891. Format:
  892. clientNum location health armor weapon powerups
  893. ==================
  894. */
  895. void TeamplayInfoMessage( gentity_t *ent ) {
  896. char entry[1024];
  897. char string[8192];
  898. int stringlength;
  899. int i, j;
  900. gentity_t *player;
  901. int cnt;
  902. int h, a;
  903. int clients[TEAM_MAXOVERLAY];
  904. if ( ! ent->client->pers.teamInfo )
  905. return;
  906. // figure out what client should be on the display
  907. // we are limited to 8, but we want to use the top eight players
  908. // but in client order (so they don't keep changing position on the overlay)
  909. for (i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++) {
  910. player = g_entities + level.sortedClients[i];
  911. if (player->inuse && player->client->sess.sessionTeam ==
  912. ent->client->sess.sessionTeam ) {
  913. clients[cnt++] = level.sortedClients[i];
  914. }
  915. }
  916. // We have the top eight players, sort them by clientNum
  917. qsort( clients, cnt, sizeof( clients[0] ), SortClients );
  918. // send the latest information on all clients
  919. string[0] = 0;
  920. stringlength = 0;
  921. for (i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++) {
  922. player = g_entities + i;
  923. if (player->inuse && player->client->sess.sessionTeam ==
  924. ent->client->sess.sessionTeam ) {
  925. h = player->client->ps.stats[STAT_HEALTH];
  926. a = player->client->ps.stats[STAT_ARMOR];
  927. if (h < 0) h = 0;
  928. if (a < 0) a = 0;
  929. Com_sprintf (entry, sizeof(entry),
  930. " %i %i %i %i %i %i",
  931. // level.sortedClients[i], player->client->pers.teamState.location, h, a,
  932. i, player->client->pers.teamState.location, h, a,
  933. player->client->ps.weapon, player->s.powerups);
  934. j = strlen(entry);
  935. if (stringlength + j > sizeof(string))
  936. break;
  937. strcpy (string + stringlength, entry);
  938. stringlength += j;
  939. cnt++;
  940. }
  941. }
  942. trap_SendServerCommand( ent-g_entities, va("tinfo %i %s", cnt, string) );
  943. }
  944. void CheckTeamStatus(void) {
  945. int i;
  946. gentity_t *loc, *ent;
  947. if (level.time - level.lastTeamLocationTime > TEAM_LOCATION_UPDATE_TIME) {
  948. level.lastTeamLocationTime = level.time;
  949. for (i = 0; i < g_maxclients.integer; i++) {
  950. ent = g_entities + i;
  951. if ( ent->client->pers.connected != CON_CONNECTED ) {
  952. continue;
  953. }
  954. if (ent->inuse && (ent->client->sess.sessionTeam == TEAM_RED || ent->client->sess.sessionTeam == TEAM_BLUE)) {
  955. loc = Team_GetLocation( ent );
  956. if (loc)
  957. ent->client->pers.teamState.location = loc->health;
  958. else
  959. ent->client->pers.teamState.location = 0;
  960. }
  961. }
  962. for (i = 0; i < g_maxclients.integer; i++) {
  963. ent = g_entities + i;
  964. if ( ent->client->pers.connected != CON_CONNECTED ) {
  965. continue;
  966. }
  967. if (ent->inuse && (ent->client->sess.sessionTeam == TEAM_RED || ent->client->sess.sessionTeam == TEAM_BLUE)) {
  968. TeamplayInfoMessage( ent );
  969. }
  970. }
  971. }
  972. }
  973. /*-----------------------------------------------------------------*/
  974. /*QUAKED team_CTF_redplayer (1 0 0) (-16 -16 -16) (16 16 32)
  975. Only in CTF games. Red players spawn here at game start.
  976. */
  977. void SP_team_CTF_redplayer( gentity_t *ent ) {
  978. }
  979. /*QUAKED team_CTF_blueplayer (0 0 1) (-16 -16 -16) (16 16 32)
  980. Only in CTF games. Blue players spawn here at game start.
  981. */
  982. void SP_team_CTF_blueplayer( gentity_t *ent ) {
  983. }
  984. /*QUAKED team_CTF_redspawn (1 0 0) (-16 -16 -24) (16 16 32)
  985. potential spawning position for red team in CTF games.
  986. Targets will be fired when someone spawns in on them.
  987. */
  988. void SP_team_CTF_redspawn(gentity_t *ent) {
  989. }
  990. /*QUAKED team_CTF_bluespawn (0 0 1) (-16 -16 -24) (16 16 32)
  991. potential spawning position for blue team in CTF games.
  992. Targets will be fired when someone spawns in on them.
  993. */
  994. void SP_team_CTF_bluespawn(gentity_t *ent) {
  995. }
  996. #ifdef MISSIONPACK
  997. /*
  998. ================
  999. Obelisks
  1000. ================
  1001. */
  1002. static void ObeliskRegen( gentity_t *self ) {
  1003. self->nextthink = level.time + g_obeliskRegenPeriod.integer * 1000;
  1004. if( self->health >= g_obeliskHealth.integer ) {
  1005. return;
  1006. }
  1007. G_AddEvent( self, EV_POWERUP_REGEN, 0 );
  1008. self->health += g_obeliskRegenAmount.integer;
  1009. if ( self->health > g_obeliskHealth.integer ) {
  1010. self->health = g_obeliskHealth.integer;
  1011. }
  1012. self->activator->s.modelindex2 = self->health * 0xff / g_obeliskHealth.integer;
  1013. self->activator->s.frame = 0;
  1014. }
  1015. static void ObeliskRespawn( gentity_t *self ) {
  1016. self->takedamage = qtrue;
  1017. self->health = g_obeliskHealth.integer;
  1018. self->think = ObeliskRegen;
  1019. self->nextthink = level.time + g_obeliskRegenPeriod.integer * 1000;
  1020. self->activator->s.frame = 0;
  1021. }
  1022. static void ObeliskDie( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
  1023. int otherTeam;
  1024. otherTeam = OtherTeam( self->spawnflags );
  1025. AddTeamScore(self->s.pos.trBase, otherTeam, 1);
  1026. Team_ForceGesture(otherTeam);
  1027. CalculateRanks();
  1028. self->takedamage = qfalse;
  1029. self->think = ObeliskRespawn;
  1030. self->nextthink = level.time + g_obeliskRespawnDelay.integer * 1000;
  1031. self->activator->s.modelindex2 = 0xff;
  1032. self->activator->s.frame = 2;
  1033. G_AddEvent( self->activator, EV_OBELISKEXPLODE, 0 );
  1034. AddScore(attacker, self->r.currentOrigin, CTF_CAPTURE_BONUS);
  1035. // add the sprite over the player's head
  1036. attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
  1037. attacker->client->ps.eFlags |= EF_AWARD_CAP;
  1038. attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
  1039. attacker->client->ps.persistant[PERS_CAPTURES]++;
  1040. teamgame.redObeliskAttackedTime = 0;
  1041. teamgame.blueObeliskAttackedTime = 0;
  1042. }
  1043. static void ObeliskTouch( gentity_t *self, gentity_t *other, trace_t *trace ) {
  1044. int tokens;
  1045. if ( !other->client ) {
  1046. return;
  1047. }
  1048. if ( OtherTeam(other->client->sess.sessionTeam) != self->spawnflags ) {
  1049. return;
  1050. }
  1051. tokens = other->client->ps.generic1;
  1052. if( tokens <= 0 ) {
  1053. return;
  1054. }
  1055. PrintMsg(NULL, "%s" S_COLOR_WHITE " brought in %i skull%s.\n",
  1056. other->client->pers.netname, tokens, tokens ? "s" : "" );
  1057. AddTeamScore(self->s.pos.trBase, other->client->sess.sessionTeam, tokens);
  1058. Team_ForceGesture(other->client->sess.sessionTeam);
  1059. AddScore(other, self->r.currentOrigin, CTF_CAPTURE_BONUS*tokens);
  1060. // add the sprite over the player's head
  1061. other->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
  1062. other->client->ps.eFlags |= EF_AWARD_CAP;
  1063. other->client->rewardTime = level.time + REWARD_SPRITE_TIME;
  1064. other->client->ps.persistant[PERS_CAPTURES] += tokens;
  1065. other->client->ps.generic1 = 0;
  1066. CalculateRanks();
  1067. Team_CaptureFlagSound( self, self->spawnflags );
  1068. }
  1069. static void ObeliskPain( gentity_t *self, gentity_t *attacker, int damage ) {
  1070. int actualDamage = damage / 10;
  1071. if (actualDamage <= 0) {
  1072. actualDamage = 1;
  1073. }
  1074. self->activator->s.modelindex2 = self->health * 0xff / g_obeliskHealth.integer;
  1075. if (!self->activator->s.frame) {
  1076. G_AddEvent(self, EV_OBELISKPAIN, 0);
  1077. }
  1078. self->activator->s.frame = 1;
  1079. AddScore(attacker, self->r.currentOrigin, actualDamage);
  1080. }
  1081. gentity_t *SpawnObelisk( vec3_t origin, int team, int spawnflags) {
  1082. trace_t tr;
  1083. vec3_t dest;
  1084. gentity_t *ent;
  1085. ent = G_Spawn();
  1086. VectorCopy( origin, ent->s.origin );
  1087. VectorCopy( origin, ent->s.pos.trBase );
  1088. VectorCopy( origin, ent->r.currentOrigin );
  1089. VectorSet( ent->r.mins, -15, -15, 0 );
  1090. VectorSet( ent->r.maxs, 15, 15, 87 );
  1091. ent->s.eType = ET_GENERAL;
  1092. ent->flags = FL_NO_KNOCKBACK;
  1093. if( g_gametype.integer == GT_OBELISK ) {
  1094. ent->r.contents = CONTENTS_SOLID;
  1095. ent->takedamage = qtrue;
  1096. ent->health = g_obeliskHealth.integer;
  1097. ent->die = ObeliskDie;
  1098. ent->pain = ObeliskPain;
  1099. ent->think = ObeliskRegen;
  1100. ent->nextthink = level.time + g_obeliskRegenPeriod.integer * 1000;
  1101. }
  1102. if( g_gametype.integer == GT_HARVESTER ) {
  1103. ent->r.contents = CONTENTS_TRIGGER;
  1104. ent->touch = ObeliskTouch;
  1105. }
  1106. if ( spawnflags & 1 ) {
  1107. // suspended
  1108. G_SetOrigin( ent, ent->s.origin );
  1109. } else {
  1110. // mappers like to put them exactly on the floor, but being coplanar
  1111. // will sometimes show up as starting in solid, so lif it up one pixel
  1112. ent->s.origin[2] += 1;
  1113. // drop to floor
  1114. VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
  1115. trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
  1116. if ( tr.startsolid ) {
  1117. ent->s.origin[2] -= 1;
  1118. G_Printf( "SpawnObelisk: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin) );
  1119. ent->s.groundEntityNum = ENTITYNUM_NONE;
  1120. G_SetOrigin( ent, ent->s.origin );
  1121. }
  1122. else {
  1123. // allow to ride movers
  1124. ent->s.groundEntityNum = tr.entityNum;
  1125. G_SetOrigin( ent, tr.endpos );
  1126. }
  1127. }
  1128. ent->spawnflags = team;
  1129. trap_LinkEntity( ent );
  1130. return ent;
  1131. }
  1132. /*QUAKED team_redobelisk (1 0 0) (-16 -16 0) (16 16 8)
  1133. */
  1134. void SP_team_redobelisk( gentity_t *ent ) {
  1135. gentity_t *obelisk;
  1136. if ( g_gametype.integer <= GT_TEAM ) {
  1137. G_FreeEntity(ent);
  1138. return;
  1139. }
  1140. ent->s.eType = ET_TEAM;
  1141. if ( g_gametype.integer == GT_OBELISK ) {
  1142. obelisk = SpawnObelisk( ent->s.origin, TEAM_RED, ent->spawnflags );
  1143. obelisk->activator = ent;
  1144. // initial obelisk health value
  1145. ent->s.modelindex2 = 0xff;
  1146. ent->s.frame = 0;
  1147. }
  1148. if ( g_gametype.integer == GT_HARVESTER ) {
  1149. obelisk = SpawnObelisk( ent->s.origin, TEAM_RED, ent->spawnflags );
  1150. obelisk->activator = ent;
  1151. }
  1152. ent->s.modelindex = TEAM_RED;
  1153. trap_LinkEntity(ent);
  1154. }
  1155. /*QUAKED team_blueobelisk (0 0 1) (-16 -16 0) (16 16 88)
  1156. */
  1157. void SP_team_blueobelisk( gentity_t *ent ) {
  1158. gentity_t *obelisk;
  1159. if ( g_gametype.integer <= GT_TEAM ) {
  1160. G_FreeEntity(ent);
  1161. return;
  1162. }
  1163. ent->s.eType = ET_TEAM;
  1164. if ( g_gametype.integer == GT_OBELISK ) {
  1165. obelisk = SpawnObelisk( ent->s.origin, TEAM_BLUE, ent->spawnflags );
  1166. obelisk->activator = ent;
  1167. // initial obelisk health value
  1168. ent->s.modelindex2 = 0xff;
  1169. ent->s.frame = 0;
  1170. }
  1171. if ( g_gametype.integer == GT_HARVESTER ) {
  1172. obelisk = SpawnObelisk( ent->s.origin, TEAM_BLUE, ent->spawnflags );
  1173. obelisk->activator = ent;
  1174. }
  1175. ent->s.modelindex = TEAM_BLUE;
  1176. trap_LinkEntity(ent);
  1177. }
  1178. /*QUAKED team_neutralobelisk (0 0 1) (-16 -16 0) (16 16 88)
  1179. */
  1180. void SP_team_neutralobelisk( gentity_t *ent ) {
  1181. if ( g_gametype.integer != GT_1FCTF && g_gametype.integer != GT_HARVESTER ) {
  1182. G_FreeEntity(ent);
  1183. return;
  1184. }
  1185. ent->s.eType = ET_TEAM;
  1186. if ( g_gametype.integer == GT_HARVESTER) {
  1187. neutralObelisk = SpawnObelisk( ent->s.origin, TEAM_FREE, ent->spawnflags);
  1188. neutralObelisk->spawnflags = TEAM_FREE;
  1189. }
  1190. ent->s.modelindex = TEAM_FREE;
  1191. trap_LinkEntity(ent);
  1192. }
  1193. /*
  1194. ================
  1195. CheckObeliskAttack
  1196. ================
  1197. */
  1198. qboolean CheckObeliskAttack( gentity_t *obelisk, gentity_t *attacker ) {
  1199. gentity_t *te;
  1200. // if this really is an obelisk
  1201. if( obelisk->die != ObeliskDie ) {
  1202. return qfalse;
  1203. }
  1204. // if the attacker is a client
  1205. if( !attacker->client ) {
  1206. return qfalse;
  1207. }
  1208. // if the obelisk is on the same team as the attacker then don't hurt it
  1209. if( obelisk->spawnflags == attacker->client->sess.sessionTeam ) {
  1210. return qtrue;
  1211. }
  1212. // obelisk may be hurt
  1213. // if not played any sounds recently
  1214. if ((obelisk->spawnflags == TEAM_RED &&
  1215. teamgame.redObeliskAttackedTime < level.time - OVERLOAD_ATTACK_BASE_SOUND_TIME) ||
  1216. (obelisk->spawnflags == TEAM_BLUE &&
  1217. teamgame.blueObeliskAttackedTime < level.time - OVERLOAD_ATTACK_BASE_SOUND_TIME) ) {
  1218. // tell which obelisk is under attack
  1219. te = G_TempEntity( obelisk->s.pos.trBase, EV_GLOBAL_TEAM_SOUND );
  1220. if( obelisk->spawnflags == TEAM_RED ) {
  1221. te->s.eventParm = GTS_REDOBELISK_ATTACKED;
  1222. teamgame.redObeliskAttackedTime = level.time;
  1223. }
  1224. else {
  1225. te->s.eventParm = GTS_BLUEOBELISK_ATTACKED;
  1226. teamgame.blueObeliskAttackedTime = level.time;
  1227. }
  1228. te->r.svFlags |= SVF_BROADCAST;
  1229. }
  1230. return qfalse;
  1231. }
  1232. #endif