PageRenderTime 72ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/codemp/game/g_saga.c

https://github.com/Stoiss/jaMME
C | 1963 lines | 1467 code | 298 blank | 198 comment | 364 complexity | 56a5b9fdbbdade30a1f90900af292363 MD5 | raw file
Possible License(s): GPL-2.0
  1. // Copyright (C) 2000-2002 Raven Software, Inc.
  2. //
  3. /*****************************************************************************
  4. * name: g_saga.c
  5. *
  6. * desc: Game-side module for Siege gametype.
  7. *
  8. * $Author: Rich Whitehouse $
  9. * $Revision: 1.6 $
  10. *
  11. *****************************************************************************/
  12. #include "g_local.h"
  13. #include "bg_saga.h"
  14. #define SIEGEITEM_STARTOFFRADAR 8
  15. static char team1[512];
  16. static char team2[512];
  17. siegePers_t g_siegePersistant = {qfalse, 0, 0};
  18. int imperial_goals_required = 0;
  19. int imperial_goals_completed = 0;
  20. int rebel_goals_required = 0;
  21. int rebel_goals_completed = 0;
  22. int imperial_time_limit = 0;
  23. int rebel_time_limit = 0;
  24. int gImperialCountdown = 0;
  25. int gRebelCountdown = 0;
  26. int rebel_attackers = 0;
  27. int imperial_attackers = 0;
  28. qboolean gSiegeRoundBegun = qfalse;
  29. qboolean gSiegeRoundEnded = qfalse;
  30. qboolean gSiegeRoundWinningTeam = 0;
  31. int gSiegeBeginTime = Q3_INFINITE;
  32. int g_preroundState = 0; //default to starting as spec (1 is starting ingame)
  33. void LogExit( const char *string );
  34. void SetTeamQuick(gentity_t *ent, int team, qboolean doBegin);
  35. static char gParseObjectives[MAX_SIEGE_INFO_SIZE];
  36. static char gObjectiveCfgStr[1024];
  37. //go through all classes on a team and register their
  38. //weapons and items for precaching.
  39. void G_SiegeRegisterWeaponsAndHoldables(int team)
  40. {
  41. siegeTeam_t *stm = BG_SiegeFindThemeForTeam(team);
  42. if (stm)
  43. {
  44. int i = 0;
  45. siegeClass_t *scl;
  46. while (i < stm->numClasses)
  47. {
  48. scl = stm->classes[i];
  49. if (scl)
  50. {
  51. int j = 0;
  52. while (j < WP_NUM_WEAPONS)
  53. {
  54. if (scl->weapons & (1 << j))
  55. { //we use this weapon so register it.
  56. RegisterItem(BG_FindItemForWeapon(j));
  57. }
  58. j++;
  59. }
  60. j = 0;
  61. while (j < HI_NUM_HOLDABLE)
  62. {
  63. if (scl->invenItems & (1 << j))
  64. { //we use this item so register it.
  65. RegisterItem(BG_FindItemForHoldable(j));
  66. }
  67. j++;
  68. }
  69. }
  70. i++;
  71. }
  72. }
  73. }
  74. //tell clients that this team won and print it on their scoreboard for intermission
  75. //or whatever.
  76. void SiegeSetCompleteData(int team)
  77. {
  78. trap_SetConfigstring(CS_SIEGE_WINTEAM, va("%i", team));
  79. }
  80. void InitSiegeMode(void)
  81. {
  82. vmCvar_t mapname;
  83. char levelname[512];
  84. char teamIcon[128];
  85. char goalreq[64];
  86. char teams[2048];
  87. static char objective[MAX_SIEGE_INFO_SIZE];
  88. char objecStr[8192];
  89. int len = 0;
  90. int i = 0;
  91. // int j = 0;
  92. int objectiveNumTeam1 = 0;
  93. int objectiveNumTeam2 = 0;
  94. fileHandle_t f;
  95. objective[0] = '\0';
  96. if (level.gametype != GT_SIEGE)
  97. {
  98. goto failure;
  99. }
  100. //reset
  101. SiegeSetCompleteData(0);
  102. //get pers data in case it existed from last level
  103. if (g_siegeTeamSwitch.integer)
  104. {
  105. trap_SiegePersGet(&g_siegePersistant);
  106. if (g_siegePersistant.beatingTime)
  107. {
  108. trap_SetConfigstring(CS_SIEGE_TIMEOVERRIDE, va("%i", g_siegePersistant.lastTime));
  109. }
  110. else
  111. {
  112. trap_SetConfigstring(CS_SIEGE_TIMEOVERRIDE, "0");
  113. }
  114. }
  115. else
  116. { //hmm, ok, nothing.
  117. trap_SetConfigstring(CS_SIEGE_TIMEOVERRIDE, "0");
  118. }
  119. imperial_goals_completed = 0;
  120. rebel_goals_completed = 0;
  121. trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM );
  122. Com_sprintf(levelname, sizeof(levelname), "maps/%s.siege\0", mapname.string);
  123. if ( !levelname[0] )
  124. {
  125. goto failure;
  126. }
  127. len = trap_FS_FOpenFile(levelname, &f, FS_READ);
  128. if (!f || len >= MAX_SIEGE_INFO_SIZE)
  129. {
  130. goto failure;
  131. }
  132. trap_FS_Read(siege_info, len, f);
  133. trap_FS_FCloseFile(f);
  134. siege_valid = 1;
  135. //See if players should be specs or ingame preround
  136. if (BG_SiegeGetPairedValue(siege_info, "preround_state", teams))
  137. {
  138. if (teams[0])
  139. {
  140. g_preroundState = atoi(teams);
  141. }
  142. }
  143. if (BG_SiegeGetValueGroup(siege_info, "Teams", teams))
  144. {
  145. if (g_siegeTeam1.string[0] && Q_stricmp(g_siegeTeam1.string, "none"))
  146. { //check for override
  147. strcpy(team1, g_siegeTeam1.string);
  148. }
  149. else
  150. { //otherwise use level default
  151. BG_SiegeGetPairedValue(teams, "team1", team1);
  152. }
  153. if (g_siegeTeam2.string[0] && Q_stricmp(g_siegeTeam2.string, "none"))
  154. { //check for override
  155. strcpy(team2, g_siegeTeam2.string);
  156. }
  157. else
  158. { //otherwise use level default
  159. BG_SiegeGetPairedValue(teams, "team2", team2);
  160. }
  161. }
  162. else
  163. {
  164. G_Error("Siege teams not defined");
  165. }
  166. if (BG_SiegeGetValueGroup(siege_info, team2, gParseObjectives))
  167. {
  168. if (BG_SiegeGetPairedValue(gParseObjectives, "TeamIcon", teamIcon))
  169. {
  170. trap_Cvar_Set( "team2_icon", teamIcon);
  171. }
  172. if (BG_SiegeGetPairedValue(gParseObjectives, "RequiredObjectives", goalreq))
  173. {
  174. rebel_goals_required = atoi(goalreq);
  175. }
  176. if (BG_SiegeGetPairedValue(gParseObjectives, "Timed", goalreq))
  177. {
  178. rebel_time_limit = atoi(goalreq)*1000;
  179. if (g_siegeTeamSwitch.integer &&
  180. g_siegePersistant.beatingTime)
  181. {
  182. gRebelCountdown = level.time + g_siegePersistant.lastTime;
  183. }
  184. else
  185. {
  186. gRebelCountdown = level.time + rebel_time_limit;
  187. }
  188. }
  189. if (BG_SiegeGetPairedValue(gParseObjectives, "attackers", goalreq))
  190. {
  191. rebel_attackers = atoi(goalreq);
  192. }
  193. }
  194. if (BG_SiegeGetValueGroup(siege_info, team1, gParseObjectives))
  195. {
  196. if (BG_SiegeGetPairedValue(gParseObjectives, "TeamIcon", teamIcon))
  197. {
  198. trap_Cvar_Set( "team1_icon", teamIcon);
  199. }
  200. if (BG_SiegeGetPairedValue(gParseObjectives, "RequiredObjectives", goalreq))
  201. {
  202. imperial_goals_required = atoi(goalreq);
  203. }
  204. if (BG_SiegeGetPairedValue(gParseObjectives, "Timed", goalreq))
  205. {
  206. if (rebel_time_limit)
  207. {
  208. Com_Printf("Tried to set imperial time limit, but there's already a rebel time limit!\nOnly one team can have a time limit.\n");
  209. }
  210. else
  211. {
  212. imperial_time_limit = atoi(goalreq)*1000;
  213. if (g_siegeTeamSwitch.integer &&
  214. g_siegePersistant.beatingTime)
  215. {
  216. gImperialCountdown = level.time + g_siegePersistant.lastTime;
  217. }
  218. else
  219. {
  220. gImperialCountdown = level.time + imperial_time_limit;
  221. }
  222. }
  223. }
  224. if (BG_SiegeGetPairedValue(gParseObjectives, "attackers", goalreq))
  225. {
  226. imperial_attackers = atoi(goalreq);
  227. }
  228. }
  229. //Load the player class types
  230. BG_SiegeLoadClasses(NULL);
  231. if (!bgNumSiegeClasses)
  232. { //We didn't find any?!
  233. G_Error("Couldn't find any player classes for Siege");
  234. }
  235. /*
  236. //We could probably just see what teams are used on this level,
  237. //then see what classes are used by those teams, and then precache
  238. //all weapons for said classes. However, I'm just going to do them
  239. //all for now.
  240. while (i < bgNumSiegeClasses)
  241. {
  242. cl = &bgSiegeClasses[i];
  243. j = 0;
  244. while (j < WP_NUM_WEAPONS)
  245. {
  246. if (cl->weapons & (1 << j))
  247. { //we use this weapon so register it.
  248. RegisterItem(BG_FindItemForWeapon(j));
  249. }
  250. j++;
  251. }
  252. i++;
  253. }
  254. */
  255. //Ok, I'm adding inventory item precaching now, so I'm finally going to optimize this
  256. //to only do weapons/items for the current teams used on the level.
  257. //Now load the teams since we have class data.
  258. BG_SiegeLoadTeams();
  259. if (!bgNumSiegeTeams)
  260. { //React same as with classes.
  261. G_Error("Couldn't find any player teams for Siege");
  262. }
  263. //Get and set the team themes for each team. This will control which classes can be
  264. //used on each team.
  265. if (BG_SiegeGetValueGroup(siege_info, team1, gParseObjectives))
  266. {
  267. if (BG_SiegeGetPairedValue(gParseObjectives, "UseTeam", goalreq))
  268. {
  269. BG_SiegeSetTeamTheme(SIEGETEAM_TEAM1, goalreq);
  270. }
  271. //Now count up the objectives for this team.
  272. i = 1;
  273. strcpy(objecStr, va("Objective%i", i));
  274. while (BG_SiegeGetValueGroup(gParseObjectives, objecStr, objective))
  275. {
  276. objectiveNumTeam1++;
  277. i++;
  278. strcpy(objecStr, va("Objective%i", i));
  279. }
  280. }
  281. if (BG_SiegeGetValueGroup(siege_info, team2, gParseObjectives))
  282. {
  283. if (BG_SiegeGetPairedValue(gParseObjectives, "UseTeam", goalreq))
  284. {
  285. BG_SiegeSetTeamTheme(SIEGETEAM_TEAM2, goalreq);
  286. }
  287. //Now count up the objectives for this team.
  288. i = 1;
  289. strcpy(objecStr, va("Objective%i", i));
  290. while (BG_SiegeGetValueGroup(gParseObjectives, objecStr, objective))
  291. {
  292. objectiveNumTeam2++;
  293. i++;
  294. strcpy(objecStr, va("Objective%i", i));
  295. }
  296. }
  297. //Set the configstring to show status of all current objectives
  298. strcpy(gObjectiveCfgStr, "t1");
  299. while (objectiveNumTeam1 > 0)
  300. { //mark them all as not completed since we just initialized
  301. Q_strcat(gObjectiveCfgStr, 1024, "-0");
  302. objectiveNumTeam1--;
  303. }
  304. //Finished doing team 1's objectives, now do team 2's
  305. Q_strcat(gObjectiveCfgStr, 1024, "|t2");
  306. while (objectiveNumTeam2 > 0)
  307. {
  308. Q_strcat(gObjectiveCfgStr, 1024, "-0");
  309. objectiveNumTeam2--;
  310. }
  311. //And finally set the actual config string
  312. trap_SetConfigstring(CS_SIEGE_OBJECTIVES, gObjectiveCfgStr);
  313. //precache saber data for classes that use sabers on both teams
  314. BG_PrecacheSabersForSiegeTeam(SIEGETEAM_TEAM1);
  315. BG_PrecacheSabersForSiegeTeam(SIEGETEAM_TEAM2);
  316. G_SiegeRegisterWeaponsAndHoldables(SIEGETEAM_TEAM1);
  317. G_SiegeRegisterWeaponsAndHoldables(SIEGETEAM_TEAM2);
  318. return;
  319. failure:
  320. siege_valid = 0;
  321. }
  322. void G_SiegeSetObjectiveComplete(int team, int objective, qboolean failIt)
  323. {
  324. char *p = NULL;
  325. int onObjective = 0;
  326. if (team == SIEGETEAM_TEAM1)
  327. {
  328. p = strstr(gObjectiveCfgStr, "t1");
  329. }
  330. else if (team == SIEGETEAM_TEAM2)
  331. {
  332. p = strstr(gObjectiveCfgStr, "t2");
  333. }
  334. if (!p)
  335. {
  336. assert(0);
  337. return;
  338. }
  339. //Parse from the beginning of this team's objectives until we get to the desired objective
  340. //number.
  341. while (p && *p && *p != '|')
  342. {
  343. if (*p == '-')
  344. {
  345. onObjective++;
  346. }
  347. if (onObjective == objective)
  348. { //this is the one we want
  349. //Move to the next char, the status of this objective
  350. p++;
  351. //Now change it from '0' to '1' if we are completeing the objective
  352. //or vice versa if the objective has been taken away
  353. if (failIt)
  354. {
  355. *p = '0';
  356. }
  357. else
  358. {
  359. *p = '1';
  360. }
  361. break;
  362. }
  363. p++;
  364. }
  365. //Now re-update the configstring.
  366. trap_SetConfigstring(CS_SIEGE_OBJECTIVES, gObjectiveCfgStr);
  367. }
  368. //Returns qtrue if objective complete currently, otherwise qfalse
  369. qboolean G_SiegeGetCompletionStatus(int team, int objective)
  370. {
  371. char *p = NULL;
  372. int onObjective = 0;
  373. if (team == SIEGETEAM_TEAM1)
  374. {
  375. p = strstr(gObjectiveCfgStr, "t1");
  376. }
  377. else if (team == SIEGETEAM_TEAM2)
  378. {
  379. p = strstr(gObjectiveCfgStr, "t2");
  380. }
  381. if (!p)
  382. {
  383. assert(0);
  384. return qfalse;
  385. }
  386. //Parse from the beginning of this team's objectives until we get to the desired objective
  387. //number.
  388. while (p && *p && *p != '|')
  389. {
  390. if (*p == '-')
  391. {
  392. onObjective++;
  393. }
  394. if (onObjective == objective)
  395. { //this is the one we want
  396. //Move to the next char, the status of this objective
  397. p++;
  398. //return qtrue if it's '1', qfalse if it's anything else
  399. if (*p == '1')
  400. {
  401. return qtrue;
  402. }
  403. else
  404. {
  405. return qfalse;
  406. }
  407. break;
  408. }
  409. p++;
  410. }
  411. return qfalse;
  412. }
  413. void UseSiegeTarget(gentity_t *other, gentity_t *en, char *target)
  414. { //actually use the player which triggered the object which triggered the siege objective to trigger the target
  415. gentity_t *t;
  416. gentity_t *ent;
  417. if ( !en || !en->client )
  418. { //looks like we don't have access to a player, so just use the activating entity
  419. ent = other;
  420. }
  421. else
  422. {
  423. ent = en;
  424. }
  425. if (!en)
  426. {
  427. return;
  428. }
  429. if ( !target )
  430. {
  431. return;
  432. }
  433. t = NULL;
  434. while ( (t = G_Find (t, FOFS(targetname), target)) != NULL )
  435. {
  436. if ( t == ent )
  437. {
  438. G_Printf ("WARNING: Entity used itself.\n");
  439. }
  440. else
  441. {
  442. if ( t->use )
  443. {
  444. GlobalUse(t, ent, ent);
  445. }
  446. }
  447. if ( !ent->inuse )
  448. {
  449. G_Printf("entity was removed while using targets\n");
  450. return;
  451. }
  452. }
  453. }
  454. void SiegeBroadcast_OBJECTIVECOMPLETE(int team, int client, int objective)
  455. {
  456. gentity_t *te;
  457. vec3_t nomatter;
  458. VectorClear(nomatter);
  459. te = G_TempEntity( nomatter, EV_SIEGE_OBJECTIVECOMPLETE );
  460. te->r.svFlags |= SVF_BROADCAST;
  461. te->s.eventParm = team;
  462. te->s.weapon = client;
  463. te->s.trickedentindex = objective;
  464. }
  465. void SiegeBroadcast_ROUNDOVER(int winningteam, int winningclient)
  466. {
  467. gentity_t *te;
  468. vec3_t nomatter;
  469. VectorClear(nomatter);
  470. te = G_TempEntity( nomatter, EV_SIEGE_ROUNDOVER );
  471. te->r.svFlags |= SVF_BROADCAST;
  472. te->s.eventParm = winningteam;
  473. te->s.weapon = winningclient;
  474. }
  475. void BroadcastObjectiveCompletion(int team, int objective, int final, int client)
  476. {
  477. if (client != ENTITYNUM_NONE && g_entities[client].client && g_entities[client].client->sess.sessionTeam == team)
  478. { //guy who completed this objective gets points, providing he's on the opposing team
  479. AddScore(&g_entities[client], g_entities[client].client->ps.origin, SIEGE_POINTS_OBJECTIVECOMPLETED);
  480. }
  481. SiegeBroadcast_OBJECTIVECOMPLETE(team, client, objective);
  482. //G_Printf("Broadcast goal completion team %i objective %i final %i\n", team, objective, final);
  483. }
  484. void AddSiegeWinningTeamPoints(int team, int winner)
  485. {
  486. int i = 0;
  487. gentity_t *ent;
  488. while (i < MAX_CLIENTS)
  489. {
  490. ent = &g_entities[i];
  491. if (ent && ent->client && ent->client->sess.sessionTeam == team)
  492. {
  493. if (i == winner)
  494. {
  495. AddScore(ent, ent->client->ps.origin, SIEGE_POINTS_TEAMWONROUND+SIEGE_POINTS_FINALOBJECTIVECOMPLETED);
  496. }
  497. else
  498. {
  499. AddScore(ent, ent->client->ps.origin, SIEGE_POINTS_TEAMWONROUND);
  500. }
  501. }
  502. i++;
  503. }
  504. }
  505. void SiegeClearSwitchData(void)
  506. {
  507. memset(&g_siegePersistant, 0, sizeof(g_siegePersistant));
  508. trap_SiegePersSet(&g_siegePersistant);
  509. }
  510. void SiegeDoTeamAssign(void)
  511. {
  512. int i = 0;
  513. gentity_t *ent;
  514. //yeah, this is great...
  515. while (i < MAX_CLIENTS)
  516. {
  517. ent = &g_entities[i];
  518. if (ent->inuse && ent->client &&
  519. ent->client->pers.connected == CON_CONNECTED)
  520. { //a connected client, switch his frickin teams around
  521. if (ent->client->sess.siegeDesiredTeam == SIEGETEAM_TEAM1)
  522. {
  523. ent->client->sess.siegeDesiredTeam = SIEGETEAM_TEAM2;
  524. }
  525. else if (ent->client->sess.siegeDesiredTeam == SIEGETEAM_TEAM2)
  526. {
  527. ent->client->sess.siegeDesiredTeam = SIEGETEAM_TEAM1;
  528. }
  529. if (ent->client->sess.sessionTeam == SIEGETEAM_TEAM1)
  530. {
  531. SetTeamQuick(ent, SIEGETEAM_TEAM2, qfalse);
  532. }
  533. else if (ent->client->sess.sessionTeam == SIEGETEAM_TEAM2)
  534. {
  535. SetTeamQuick(ent, SIEGETEAM_TEAM1, qfalse);
  536. }
  537. }
  538. i++;
  539. }
  540. }
  541. void SiegeTeamSwitch(int winTeam, int winTime)
  542. {
  543. trap_SiegePersGet(&g_siegePersistant);
  544. if (g_siegePersistant.beatingTime)
  545. { //was already in "switched" mode, change back
  546. //announce the winning team.
  547. //either the first team won again, or the second
  548. //team beat the time set by the initial team. In any
  549. //case the winTeam here is the overall winning team.
  550. SiegeSetCompleteData(winTeam);
  551. SiegeClearSwitchData();
  552. }
  553. else
  554. { //go into "beat their time" mode
  555. g_siegePersistant.beatingTime = qtrue;
  556. g_siegePersistant.lastTeam = winTeam;
  557. g_siegePersistant.lastTime = winTime;
  558. trap_SiegePersSet(&g_siegePersistant);
  559. }
  560. }
  561. void SiegeRoundComplete(int winningteam, int winningclient)
  562. {
  563. vec3_t nomatter;
  564. char teamstr[1024];
  565. int originalWinningClient = winningclient;
  566. //G_Printf("Team %i won\n", winningteam);
  567. if (winningclient != ENTITYNUM_NONE && g_entities[winningclient].client &&
  568. g_entities[winningclient].client->sess.sessionTeam != winningteam)
  569. { //this person just won the round for the other team..
  570. winningclient = ENTITYNUM_NONE;
  571. }
  572. VectorClear(nomatter);
  573. SiegeBroadcast_ROUNDOVER(winningteam, winningclient);
  574. AddSiegeWinningTeamPoints(winningteam, winningclient);
  575. //Instead of exiting like this, fire off a target, and let it handle things.
  576. //Can be a script or whatever the designer wants.
  577. if (winningteam == SIEGETEAM_TEAM1)
  578. {
  579. Com_sprintf(teamstr, sizeof(teamstr), team1);
  580. }
  581. else
  582. {
  583. Com_sprintf(teamstr, sizeof(teamstr), team2);
  584. }
  585. trap_SetConfigstring(CS_SIEGE_STATE, va("3|%i", level.time)); //ended
  586. gSiegeRoundBegun = qfalse;
  587. gSiegeRoundEnded = qtrue;
  588. gSiegeRoundWinningTeam = winningteam;
  589. if (BG_SiegeGetValueGroup(siege_info, teamstr, gParseObjectives))
  590. {
  591. if (!BG_SiegeGetPairedValue(gParseObjectives, "roundover_target", teamstr))
  592. { //didn't find the name of the thing to target upon win, just logexit now then.
  593. LogExit( "Objectives completed" );
  594. return;
  595. }
  596. if (originalWinningClient == ENTITYNUM_NONE)
  597. { //oh well, just find something active and use it then.
  598. int i = 0;
  599. gentity_t *ent;
  600. while (i < MAX_CLIENTS)
  601. {
  602. ent = &g_entities[i];
  603. if (ent->inuse)
  604. { //sure, you'll do.
  605. originalWinningClient = ent->s.number;
  606. break;
  607. }
  608. i++;
  609. }
  610. }
  611. G_UseTargets2(&g_entities[originalWinningClient], &g_entities[originalWinningClient], teamstr);
  612. }
  613. if (g_siegeTeamSwitch.integer &&
  614. (imperial_time_limit || rebel_time_limit))
  615. { //handle stupid team switching crap
  616. int time = 0;
  617. if (imperial_time_limit)
  618. {
  619. time = imperial_time_limit-(gImperialCountdown-level.time);
  620. }
  621. else if (rebel_time_limit)
  622. {
  623. time = rebel_time_limit-(gRebelCountdown-level.time);
  624. }
  625. if (time < 1)
  626. {
  627. time = 1;
  628. }
  629. SiegeTeamSwitch(winningteam, time);
  630. }
  631. else
  632. { //assure it's clear for next round
  633. SiegeClearSwitchData();
  634. }
  635. }
  636. void G_ValidateSiegeClassForTeam(gentity_t *ent, int team)
  637. {
  638. siegeClass_t *scl;
  639. siegeTeam_t *stm;
  640. int newClassIndex = -1;
  641. if (ent->client->siegeClass == -1)
  642. { //uh.. sure.
  643. return;
  644. }
  645. scl = &bgSiegeClasses[ent->client->siegeClass];
  646. stm = BG_SiegeFindThemeForTeam(team);
  647. if (stm)
  648. {
  649. int i = 0;
  650. while (i < stm->numClasses)
  651. { //go through the team and see its valid classes, can we find one that matches our current player class?
  652. if (stm->classes[i])
  653. {
  654. if (!Q_stricmp(scl->name, stm->classes[i]->name))
  655. { //the class we're using is already ok for this team.
  656. return;
  657. }
  658. if (stm->classes[i]->playerClass == scl->playerClass ||
  659. newClassIndex == -1)
  660. {
  661. newClassIndex = i;
  662. }
  663. }
  664. i++;
  665. }
  666. if (newClassIndex != -1)
  667. { //ok, let's find it in the global class array
  668. ent->client->siegeClass = BG_SiegeFindClassIndexByName(stm->classes[newClassIndex]->name);
  669. strcpy(ent->client->sess.siegeClass, stm->classes[newClassIndex]->name);
  670. }
  671. }
  672. }
  673. //bypass most of the normal checks in SetTeam
  674. void SetTeamQuick(gentity_t *ent, int team, qboolean doBegin)
  675. {
  676. char userinfo[MAX_INFO_STRING];
  677. trap_GetUserinfo( ent->s.number, userinfo, sizeof( userinfo ) );
  678. if (level.gametype == GT_SIEGE)
  679. {
  680. G_ValidateSiegeClassForTeam(ent, team);
  681. }
  682. ent->client->sess.sessionTeam = team;
  683. if (team == TEAM_SPECTATOR)
  684. {
  685. ent->client->sess.spectatorState = SPECTATOR_FREE;
  686. Info_SetValueForKey(userinfo, "team", "s");
  687. }
  688. else
  689. {
  690. ent->client->sess.spectatorState = SPECTATOR_NOT;
  691. if (team == TEAM_RED)
  692. {
  693. Info_SetValueForKey(userinfo, "team", "r");
  694. }
  695. else if (team == TEAM_BLUE)
  696. {
  697. Info_SetValueForKey(userinfo, "team", "b");
  698. }
  699. else
  700. {
  701. Info_SetValueForKey(userinfo, "team", "?");
  702. }
  703. }
  704. trap_SetUserinfo( ent->s.number, userinfo );
  705. ent->client->sess.spectatorClient = 0;
  706. ent->client->pers.teamState.state = TEAM_BEGIN;
  707. if ( !ClientUserinfoChanged( ent->s.number ) )
  708. return;
  709. if (doBegin)
  710. {
  711. ClientBegin( ent->s.number, qfalse );
  712. }
  713. }
  714. void SiegeRespawn(gentity_t *ent)
  715. {
  716. gentity_t *tent;
  717. if (ent->client->sess.sessionTeam != ent->client->sess.siegeDesiredTeam)
  718. {
  719. SetTeamQuick(ent, ent->client->sess.siegeDesiredTeam, qtrue);
  720. }
  721. else
  722. {
  723. ClientSpawn(ent);
  724. // add a teleportation effect
  725. tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_IN );
  726. tent->s.clientNum = ent->s.clientNum;
  727. }
  728. }
  729. void SiegeBeginRound(int entNum)
  730. { //entNum is just used as something to fire targets from.
  731. char targname[1024];
  732. if (!g_preroundState)
  733. { //if players are not ingame on round start then respawn them now
  734. int i = 0;
  735. gentity_t *ent;
  736. qboolean spawnEnt = qfalse;
  737. //respawn everyone now
  738. while (i < MAX_CLIENTS)
  739. {
  740. ent = &g_entities[i];
  741. if (ent->inuse && ent->client)
  742. {
  743. if (ent->client->sess.sessionTeam != TEAM_SPECTATOR &&
  744. !(ent->client->ps.pm_flags & PMF_FOLLOW))
  745. { //not a spec, just respawn them
  746. spawnEnt = qtrue;
  747. }
  748. else if (ent->client->sess.sessionTeam == TEAM_SPECTATOR &&
  749. (ent->client->sess.siegeDesiredTeam == TEAM_RED ||
  750. ent->client->sess.siegeDesiredTeam == TEAM_BLUE))
  751. { //spectator but has a desired team
  752. spawnEnt = qtrue;
  753. }
  754. }
  755. if (spawnEnt)
  756. {
  757. SiegeRespawn(ent);
  758. spawnEnt = qfalse;
  759. }
  760. i++;
  761. }
  762. }
  763. //Now check if there's something to fire off at the round start, if so do it.
  764. if (BG_SiegeGetPairedValue(siege_info, "roundbegin_target", targname))
  765. {
  766. if (targname[0])
  767. {
  768. G_UseTargets2(&g_entities[entNum], &g_entities[entNum], targname);
  769. }
  770. }
  771. trap_SetConfigstring(CS_SIEGE_STATE, va("0|%i", level.time)); //we're ready to g0g0g0
  772. }
  773. void SiegeCheckTimers(void)
  774. {
  775. int i=0;
  776. gentity_t *ent;
  777. int numTeam1 = 0;
  778. int numTeam2 = 0;
  779. if (level.gametype != GT_SIEGE)
  780. {
  781. return;
  782. }
  783. if (level.intermissiontime)
  784. {
  785. return;
  786. }
  787. if (gSiegeRoundEnded)
  788. {
  789. return;
  790. }
  791. if (!gSiegeRoundBegun)
  792. { //check if anyone is active on this team - if not, keep the timer set up.
  793. i = 0;
  794. while (i < MAX_CLIENTS)
  795. {
  796. ent = &g_entities[i];
  797. if (ent && ent->inuse && ent->client &&
  798. ent->client->pers.connected == CON_CONNECTED &&
  799. ent->client->sess.siegeDesiredTeam == SIEGETEAM_TEAM1)
  800. {
  801. numTeam1++;
  802. }
  803. i++;
  804. }
  805. i = 0;
  806. while (i < MAX_CLIENTS)
  807. {
  808. ent = &g_entities[i];
  809. if (ent && ent->inuse && ent->client &&
  810. ent->client->pers.connected == CON_CONNECTED &&
  811. ent->client->sess.siegeDesiredTeam == SIEGETEAM_TEAM2)
  812. {
  813. numTeam2++;
  814. }
  815. i++;
  816. }
  817. if (g_siegeTeamSwitch.integer &&
  818. g_siegePersistant.beatingTime)
  819. {
  820. gImperialCountdown = level.time + g_siegePersistant.lastTime;
  821. gRebelCountdown = level.time + g_siegePersistant.lastTime;
  822. }
  823. else
  824. {
  825. gImperialCountdown = level.time + imperial_time_limit;
  826. gRebelCountdown = level.time + rebel_time_limit;
  827. }
  828. }
  829. if (imperial_time_limit)
  830. { //team1
  831. if (gImperialCountdown < level.time)
  832. {
  833. SiegeRoundComplete(SIEGETEAM_TEAM2, ENTITYNUM_NONE);
  834. imperial_time_limit = 0;
  835. return;
  836. }
  837. }
  838. if (rebel_time_limit)
  839. { //team2
  840. if (gRebelCountdown < level.time)
  841. {
  842. SiegeRoundComplete(SIEGETEAM_TEAM1, ENTITYNUM_NONE);
  843. rebel_time_limit = 0;
  844. return;
  845. }
  846. }
  847. if (!gSiegeRoundBegun)
  848. {
  849. if (!numTeam1 || !numTeam2)
  850. { //don't have people on both teams yet.
  851. gSiegeBeginTime = level.time + SIEGE_ROUND_BEGIN_TIME;
  852. trap_SetConfigstring(CS_SIEGE_STATE, "1"); //"waiting for players on both teams"
  853. }
  854. else if (gSiegeBeginTime < level.time)
  855. { //mark the round as having begun
  856. gSiegeRoundBegun = qtrue;
  857. SiegeBeginRound(i); //perform any round start tasks
  858. }
  859. else if (gSiegeBeginTime > (level.time + SIEGE_ROUND_BEGIN_TIME))
  860. {
  861. gSiegeBeginTime = level.time + SIEGE_ROUND_BEGIN_TIME;
  862. }
  863. else
  864. {
  865. trap_SetConfigstring(CS_SIEGE_STATE, va("2|%i", gSiegeBeginTime - SIEGE_ROUND_BEGIN_TIME)); //getting ready to begin
  866. }
  867. }
  868. }
  869. void SiegeObjectiveCompleted(int team, int objective, int final, int client)
  870. {
  871. int goals_completed, goals_required;
  872. if (gSiegeRoundEnded)
  873. {
  874. return;
  875. }
  876. //Update the configstring status
  877. G_SiegeSetObjectiveComplete(team, objective, qfalse);
  878. if (final != -1)
  879. {
  880. if (team == SIEGETEAM_TEAM1)
  881. {
  882. imperial_goals_completed++;
  883. }
  884. else
  885. {
  886. rebel_goals_completed++;
  887. }
  888. }
  889. if (team == SIEGETEAM_TEAM1)
  890. {
  891. goals_completed = imperial_goals_completed;
  892. goals_required = imperial_goals_required;
  893. }
  894. else
  895. {
  896. goals_completed = rebel_goals_completed;
  897. goals_required = rebel_goals_required;
  898. }
  899. if (final == 1 || goals_completed >= goals_required)
  900. {
  901. SiegeRoundComplete(team, client);
  902. }
  903. else
  904. {
  905. BroadcastObjectiveCompletion(team, objective, final, client);
  906. }
  907. }
  908. void siegeTriggerUse(gentity_t *ent, gentity_t *other, gentity_t *activator)
  909. {
  910. char teamstr[64];
  911. char objectivestr[64];
  912. static char desiredobjective[MAX_SIEGE_INFO_SIZE];
  913. int clUser = ENTITYNUM_NONE;
  914. int final = 0;
  915. int i = 0;
  916. desiredobjective[0] = '\0';
  917. if (!siege_valid)
  918. {
  919. return;
  920. }
  921. if (!(ent->s.eFlags & EF_RADAROBJECT))
  922. { //toggle radar on and exit if it is not showing up already
  923. ent->s.eFlags |= EF_RADAROBJECT;
  924. return;
  925. }
  926. if (activator && activator->client)
  927. { //activator will hopefully be the person who triggered this event
  928. clUser = activator->s.number;
  929. }
  930. if (ent->side == SIEGETEAM_TEAM1)
  931. {
  932. Com_sprintf(teamstr, sizeof(teamstr), team1);
  933. }
  934. else
  935. {
  936. Com_sprintf(teamstr, sizeof(teamstr), team2);
  937. }
  938. if (BG_SiegeGetValueGroup(siege_info, teamstr, gParseObjectives))
  939. {
  940. Com_sprintf(objectivestr, sizeof(objectivestr), "Objective%i", ent->objective);
  941. if (BG_SiegeGetValueGroup(gParseObjectives, objectivestr, desiredobjective))
  942. {
  943. if (BG_SiegeGetPairedValue(desiredobjective, "final", teamstr))
  944. {
  945. final = atoi(teamstr);
  946. }
  947. if (BG_SiegeGetPairedValue(desiredobjective, "target", teamstr))
  948. {
  949. while (teamstr[i])
  950. {
  951. if (teamstr[i] == '\r' ||
  952. teamstr[i] == '\n')
  953. {
  954. teamstr[i] = '\0';
  955. }
  956. i++;
  957. }
  958. UseSiegeTarget(other, activator, teamstr);
  959. }
  960. if (ent->target && ent->target[0])
  961. { //use this too
  962. UseSiegeTarget(other, activator, ent->target);
  963. }
  964. SiegeObjectiveCompleted(ent->side, ent->objective, final, clUser);
  965. }
  966. }
  967. }
  968. /*QUAKED info_siege_objective (1 0 1) (-16 -16 -24) (16 16 32) ? x x STARTOFFRADAR
  969. STARTOFFRADAR - start not displaying on radar, don't display until used.
  970. "objective" - specifies the objective to complete upon activation
  971. "side" - set to 1 to specify an imperial goal, 2 to specify rebels
  972. "icon" - icon that represents the objective on the radar
  973. */
  974. void SP_info_siege_objective (gentity_t *ent)
  975. {
  976. char* s;
  977. if (!siege_valid || level.gametype != GT_SIEGE)
  978. {
  979. G_FreeEntity(ent);
  980. return;
  981. }
  982. ent->use = siegeTriggerUse;
  983. G_SpawnInt( "objective", "0", &ent->objective);
  984. G_SpawnInt( "side", "0", &ent->side);
  985. if (!ent->objective || !ent->side)
  986. { //j00 fux0red something up
  987. G_FreeEntity(ent);
  988. G_Printf("ERROR: info_siege_objective without an objective or side value\n");
  989. return;
  990. }
  991. //Set it up to be drawn on radar
  992. if (!(ent->spawnflags & SIEGEITEM_STARTOFFRADAR))
  993. {
  994. ent->s.eFlags |= EF_RADAROBJECT;
  995. }
  996. //All clients want to know where it is at all times for radar
  997. ent->r.svFlags |= SVF_BROADCAST;
  998. G_SpawnString( "icon", "", &s );
  999. if (s && s[0])
  1000. {
  1001. // We have an icon, so index it now. We are reusing the genericenemyindex
  1002. // variable rather than adding a new one to the entity state.
  1003. ent->s.genericenemyindex = G_IconIndex(s);
  1004. }
  1005. ent->s.brokenLimbs = ent->side;
  1006. ent->s.frame = ent->objective;
  1007. trap_LinkEntity(ent);
  1008. }
  1009. void SiegeIconUse(gentity_t *ent, gentity_t *other, gentity_t *activator)
  1010. { //toggle it on and off
  1011. if (ent->s.eFlags & EF_RADAROBJECT)
  1012. {
  1013. ent->s.eFlags &= ~EF_RADAROBJECT;
  1014. ent->r.svFlags &= ~SVF_BROADCAST;
  1015. }
  1016. else
  1017. {
  1018. ent->s.eFlags |= EF_RADAROBJECT;
  1019. ent->r.svFlags |= SVF_BROADCAST;
  1020. }
  1021. }
  1022. /*QUAKED info_siege_radaricon (1 0 1) (-16 -16 -24) (16 16 32) ?
  1023. Used to arbitrarily display radar icons at placed location. Can be used
  1024. to toggle on and off.
  1025. "icon" - icon that represents the objective on the radar
  1026. "startoff" - if 1 start off
  1027. */
  1028. void SP_info_siege_radaricon (gentity_t *ent)
  1029. {
  1030. char* s;
  1031. int i;
  1032. if (!siege_valid || level.gametype != GT_SIEGE)
  1033. {
  1034. G_FreeEntity(ent);
  1035. return;
  1036. }
  1037. G_SpawnInt("startoff", "0", &i);
  1038. if (!i)
  1039. { //start on then
  1040. ent->s.eFlags |= EF_RADAROBJECT;
  1041. ent->r.svFlags |= SVF_BROADCAST;
  1042. }
  1043. G_SpawnString( "icon", "", &s );
  1044. if (!s || !s[0])
  1045. { //that's the whole point of the entity
  1046. Com_Error(ERR_DROP, "misc_siege_radaricon without an icon");
  1047. return;
  1048. }
  1049. ent->use = SiegeIconUse;
  1050. ent->s.genericenemyindex = G_IconIndex(s);
  1051. trap_LinkEntity(ent);
  1052. }
  1053. void decompTriggerUse(gentity_t *ent, gentity_t *other, gentity_t *activator)
  1054. {
  1055. int final = 0;
  1056. char teamstr[1024];
  1057. char objectivestr[64];
  1058. static char desiredobjective[MAX_SIEGE_INFO_SIZE];
  1059. desiredobjective[0] = '\0';
  1060. if (gSiegeRoundEnded)
  1061. {
  1062. return;
  1063. }
  1064. if (!G_SiegeGetCompletionStatus(ent->side, ent->objective))
  1065. { //if it's not complete then there's nothing to do here
  1066. return;
  1067. }
  1068. //Update the configstring status
  1069. G_SiegeSetObjectiveComplete(ent->side, ent->objective, qtrue);
  1070. //Find out if this objective counts toward the final objective count
  1071. if (ent->side == SIEGETEAM_TEAM1)
  1072. {
  1073. Com_sprintf(teamstr, sizeof(teamstr), team1);
  1074. }
  1075. else
  1076. {
  1077. Com_sprintf(teamstr, sizeof(teamstr), team2);
  1078. }
  1079. if (BG_SiegeGetValueGroup(siege_info, teamstr, gParseObjectives))
  1080. {
  1081. Com_sprintf(objectivestr, sizeof(objectivestr), "Objective%i", ent->objective);
  1082. if (BG_SiegeGetValueGroup(gParseObjectives, objectivestr, desiredobjective))
  1083. {
  1084. if (BG_SiegeGetPairedValue(desiredobjective, "final", teamstr))
  1085. {
  1086. final = atoi(teamstr);
  1087. }
  1088. }
  1089. }
  1090. //Subtract the goal num if applicable
  1091. if (final != -1)
  1092. {
  1093. if (ent->side == SIEGETEAM_TEAM1)
  1094. {
  1095. imperial_goals_completed--;
  1096. }
  1097. else
  1098. {
  1099. rebel_goals_completed--;
  1100. }
  1101. }
  1102. }
  1103. /*QUAKED info_siege_decomplete (1 0 1) (-16 -16 -24) (16 16 32)
  1104. "objective" - specifies the objective to decomplete upon activation
  1105. "side" - set to 1 to specify an imperial (team1) goal, 2 to specify rebels (team2)
  1106. */
  1107. void SP_info_siege_decomplete (gentity_t *ent)
  1108. {
  1109. if (!siege_valid || level.gametype != GT_SIEGE)
  1110. {
  1111. G_FreeEntity(ent);
  1112. return;
  1113. }
  1114. ent->use = decompTriggerUse;
  1115. G_SpawnInt( "objective", "0", &ent->objective);
  1116. G_SpawnInt( "side", "0", &ent->side);
  1117. if (!ent->objective || !ent->side)
  1118. { //j00 fux0red something up
  1119. G_FreeEntity(ent);
  1120. G_Printf("ERROR: info_siege_objective_decomplete without an objective or side value\n");
  1121. return;
  1122. }
  1123. }
  1124. void siegeEndUse(gentity_t *ent, gentity_t *other, gentity_t *activator)
  1125. {
  1126. LogExit("Round ended");
  1127. }
  1128. /*QUAKED target_siege_end (1 0 1) (-16 -16 -24) (16 16 32)
  1129. Do a logexit for siege when used.
  1130. */
  1131. void SP_target_siege_end (gentity_t *ent)
  1132. {
  1133. if (!siege_valid || level.gametype != GT_SIEGE)
  1134. {
  1135. G_FreeEntity(ent);
  1136. return;
  1137. }
  1138. ent->use = siegeEndUse;
  1139. }
  1140. #define SIEGE_ITEM_RESPAWN_TIME 20000
  1141. void SiegeItemRemoveOwner(gentity_t *ent, gentity_t *carrier)
  1142. {
  1143. ent->genericValue2 = 0; //Remove picked-up flag
  1144. ent->genericValue8 = ENTITYNUM_NONE; //Mark entity carrying us as none
  1145. if (carrier)
  1146. {
  1147. carrier->client->holdingObjectiveItem = 0; //The carrier is no longer carrying us
  1148. carrier->r.svFlags &= ~SVF_BROADCAST;
  1149. }
  1150. }
  1151. static void SiegeItemRespawnEffect(gentity_t *ent, vec3_t newOrg)
  1152. {
  1153. vec3_t upAng;
  1154. if (ent->target5 && ent->target5[0])
  1155. {
  1156. G_UseTargets2(ent, ent, ent->target5);
  1157. }
  1158. if (!ent->genericValue10)
  1159. { //no respawn effect
  1160. return;
  1161. }
  1162. VectorSet(upAng, 0, 0, 1);
  1163. //Play it once on the current origin, and once on the origin we're respawning to.
  1164. G_PlayEffectID(ent->genericValue10, ent->r.currentOrigin, upAng);
  1165. G_PlayEffectID(ent->genericValue10, newOrg, upAng);
  1166. }
  1167. static void SiegeItemRespawnOnOriginalSpot(gentity_t *ent, gentity_t *carrier)
  1168. {
  1169. SiegeItemRespawnEffect(ent, ent->pos1);
  1170. G_SetOrigin(ent, ent->pos1);
  1171. SiegeItemRemoveOwner(ent, carrier);
  1172. // Stop the item from flashing on the radar
  1173. ent->s.time2 = 0;
  1174. }
  1175. void SiegeItemThink(gentity_t *ent)
  1176. {
  1177. gentity_t *carrier = NULL;
  1178. if (ent->genericValue12)
  1179. { //recharge health
  1180. if (ent->health > 0 && ent->health < ent->maxHealth && ent->genericValue14 < level.time)
  1181. {
  1182. ent->genericValue14 = level.time + ent->genericValue13;
  1183. ent->health += ent->genericValue12;
  1184. if (ent->health > ent->maxHealth)
  1185. {
  1186. ent->health = ent->maxHealth;
  1187. }
  1188. }
  1189. }
  1190. if (ent->genericValue8 != ENTITYNUM_NONE)
  1191. { //Just keep sticking it on top of the owner. We need it in the same PVS as him so it will render bolted onto him properly.
  1192. carrier = &g_entities[ent->genericValue8];
  1193. if (carrier->inuse && carrier->client)
  1194. {
  1195. VectorCopy(carrier->client->ps.origin, ent->r.currentOrigin);
  1196. trap_LinkEntity(ent);
  1197. }
  1198. }
  1199. else if (ent->genericValue1)
  1200. { //this means we want to run physics on the object
  1201. G_RunExPhys(ent, ent->radius, ent->mass, ent->random, qfalse, NULL, 0);
  1202. }
  1203. //Bolt us to whoever is carrying us if a client
  1204. if (ent->genericValue8 < MAX_CLIENTS)
  1205. {
  1206. ent->s.boltToPlayer = ent->genericValue8+1;
  1207. }
  1208. else
  1209. {
  1210. ent->s.boltToPlayer = 0;
  1211. }
  1212. if (carrier) {
  1213. //This checking can be a bit iffy on the death stuff, but in theory we should always
  1214. //get a think in before the default minimum respawn time is exceeded.
  1215. if (!carrier->inuse || !carrier->client ||
  1216. (carrier->client->sess.sessionTeam != SIEGETEAM_TEAM1 && carrier->client->sess.sessionTeam != SIEGETEAM_TEAM2) ||
  1217. (carrier->client->ps.pm_flags & PMF_FOLLOW))
  1218. { //respawn on the original spot
  1219. SiegeItemRespawnOnOriginalSpot(ent, NULL);
  1220. }
  1221. else if (carrier->health < 1)
  1222. { //The carrier died so pop out where he is (unless in nodrop).
  1223. if (ent->target6 && ent->target6[0])
  1224. {
  1225. G_UseTargets2(ent, ent, ent->target6);
  1226. }
  1227. if ( trap_PointContents(carrier->client->ps.origin, carrier->s.number) & CONTENTS_NODROP )
  1228. { //In nodrop land, go back to the original spot.
  1229. SiegeItemRespawnOnOriginalSpot(ent, carrier);
  1230. }
  1231. else
  1232. {
  1233. //perform a startsolid check to make sure the seige item doesn't get stuck
  1234. //in a wall or something
  1235. trace_t tr;
  1236. trap_Trace(&tr, carrier->client->ps.origin, ent->r.mins, ent->r.maxs, carrier->client->ps.origin, ent->s.number, ent->clipmask);
  1237. if(tr.startsolid)
  1238. {//bad spawning area, try again with the trace up a bit.
  1239. vec3_t TracePoint;
  1240. VectorCopy(carrier->client->ps.origin, TracePoint);
  1241. TracePoint[2] += 30;
  1242. trap_Trace(&tr, TracePoint, ent->r.mins, ent->r.maxs, TracePoint, ent->s.number, ent->clipmask);
  1243. if(tr.startsolid)
  1244. {//hmm, well that didn't work. try one last time with the item back
  1245. //away from where the dude was facing (in case the carrier was
  1246. //close to something they were attacking.)
  1247. vec3_t fwd;
  1248. AngleVectors(carrier->client->ps.viewangles,fwd, NULL, NULL);
  1249. VectorMA(TracePoint, -30, fwd, TracePoint);
  1250. trap_Trace(&tr, TracePoint, ent->r.mins, ent->r.maxs, TracePoint, ent->s.number, ent->clipmask);
  1251. if(tr.startsolid)
  1252. {
  1253. SiegeItemRespawnOnOriginalSpot(ent, carrier);
  1254. return;
  1255. }
  1256. }
  1257. G_SetOrigin(ent, TracePoint);
  1258. }
  1259. else
  1260. {//we're good at the player's origin
  1261. G_SetOrigin(ent, carrier->client->ps.origin);
  1262. }
  1263. //G_SetOrigin(ent, carrier->client->ps.origin);
  1264. ent->epVelocity[0] = Q_irand(-80, 80);
  1265. ent->epVelocity[1] = Q_irand(-80, 80);
  1266. ent->epVelocity[2] = Q_irand(40, 80);
  1267. //We're in a nonstandard place, so if we go this long without being touched,
  1268. //assume we may not be reachable and respawn on the original spot.
  1269. ent->genericValue9 = level.time + SIEGE_ITEM_RESPAWN_TIME;
  1270. SiegeItemRemoveOwner(ent, carrier);
  1271. }
  1272. }
  1273. }
  1274. if (ent->genericValue9 && ent->genericValue9 < level.time)
  1275. { //time to respawn on the original spot then
  1276. SiegeItemRespawnEffect(ent, ent->pos1);
  1277. G_SetOrigin(ent, ent->pos1);
  1278. ent->genericValue9 = 0;
  1279. // stop flashing on radar
  1280. ent->s.time2 = 0;
  1281. }
  1282. ent->nextthink = level.time + FRAMETIME/2;
  1283. }
  1284. void SiegeItemTouch( gentity_t *self, gentity_t *other, trace_t *trace )
  1285. {
  1286. if (!other || !other->inuse ||
  1287. !other->client || other->s.eType == ET_NPC)
  1288. {
  1289. if (trace && trace->startsolid)
  1290. { //let me out! (ideally this should not happen, but such is life)
  1291. vec3_t escapePos;
  1292. VectorCopy(self->r.currentOrigin, escapePos);
  1293. escapePos[2] += 1.0f;
  1294. //I hope you weren't stuck in the ceiling.
  1295. G_SetOrigin(self, escapePos);
  1296. }
  1297. return;
  1298. }
  1299. if (other->health < 1)
  1300. { //dead people can't pick us up.
  1301. return;
  1302. }
  1303. if (other->client->holdingObjectiveItem)
  1304. { //this guy's already carrying a siege item
  1305. return;
  1306. }
  1307. if ( other->client->ps.pm_type == PM_SPECTATOR )
  1308. {//spectators don't pick stuff up
  1309. return;
  1310. }
  1311. if (self->genericValue2)
  1312. { //Am I already picked up?
  1313. return;
  1314. }
  1315. if (self->genericValue6 == other->client->sess.sessionTeam)
  1316. { //Set to not be touchable by players on this team.
  1317. return;
  1318. }
  1319. if (!gSiegeRoundBegun)
  1320. { //can't pick it up if round hasn't started yet
  1321. return;
  1322. }
  1323. if (self->noise_index)
  1324. { //play the pickup noise.
  1325. G_Sound(other, CHAN_AUTO, self->noise_index);
  1326. }
  1327. self->genericValue2 = 1; //Mark it as picked up.
  1328. other->client->holdingObjectiveItem = self->s.number;
  1329. other->r.svFlags |= SVF_BROADCAST; //broadcast player while he carries this
  1330. self->genericValue8 = other->s.number; //Keep the index so we know who is "carrying" us
  1331. self->genericValue9 = 0; //So it doesn't think it has to respawn.
  1332. if (self->target2 && self->target2[0] && (!self->genericValue4 || !self->genericValue5))
  1333. { //fire the target for pickup, if it's set to fire every time, or set to only fire the first time and the first time has not yet occured.
  1334. G_UseTargets2(self, self, self->target2);
  1335. self->genericValue5 = 1; //mark it as having been picked up
  1336. }
  1337. // time2 set to -1 will blink the item on the radar indefinately
  1338. self->s.time2 = 0xFFFFFFFF;
  1339. }
  1340. void SiegeItemPain(gentity_t *self, gentity_t *attacker, int damage)
  1341. {
  1342. // Time 2 is used to pulse the radar icon to show its under attack
  1343. self->s.time2 = level.time;
  1344. }
  1345. void SiegeItemDie( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath )
  1346. {
  1347. self->takedamage = qfalse; //don't die more than once
  1348. if (self->genericValue3)
  1349. { //An indexed effect to play on death
  1350. vec3_t upAng;
  1351. VectorSet(upAng, 0, 0, 1);
  1352. G_PlayEffectID(self->genericValue3, self->r.currentOrigin, upAng);
  1353. }
  1354. self->neverFree = qfalse;
  1355. self->think = G_FreeEntity;
  1356. self->nextthink = level.time;
  1357. //Fire off the death target if we've got one.
  1358. if (self->target4 && self->target4[0])
  1359. {
  1360. G_UseTargets2(self, self, self->target4);
  1361. }
  1362. }
  1363. void SiegeItemUse(gentity_t *ent, gentity_t *other, gentity_t *activator)
  1364. { //once used, become active
  1365. if (ent->spawnflags & SIEGEITEM_STARTOFFRADAR)
  1366. { //start showing on radar
  1367. ent->s.eFlags |= EF_RADAROBJECT;
  1368. if (!(ent->s.eFlags & EF_NODRAW))
  1369. { //we've nothing else to do here
  1370. return;
  1371. }
  1372. }
  1373. else
  1374. { //make sure it's showing up
  1375. ent->s.eFlags |= EF_RADAROBJECT;
  1376. }
  1377. if (ent->genericValue11 || !ent->takedamage)
  1378. { //We want to be able to walk into it to pick it up then.
  1379. ent->r.contents = CONTENTS_TRIGGER;
  1380. ent->clipmask = CONTENTS_SOLID|CONTENTS_TERRAIN;
  1381. if (ent->genericValue11)
  1382. {
  1383. ent->touch = SiegeItemTouch;
  1384. }
  1385. }
  1386. else
  1387. { //Make it solid.
  1388. ent->r.contents = MASK_PLAYERSOLID;
  1389. ent->clipmask = MASK_PLAYERSOLID;
  1390. }
  1391. ent->think = SiegeItemThink;
  1392. ent->nextthink = level.time + FRAMETIME/2;
  1393. //take off nodraw
  1394. ent->s.eFlags &= ~EF_NODRAW;
  1395. if (ent->paintarget && ent->paintarget[0])
  1396. { //want to be on this guy's origin now then
  1397. gentity_t *targ = G_Find (NULL, FOFS(targetname), ent->paintarget);
  1398. if (targ && targ->inuse)
  1399. {
  1400. //perform a startsolid check to make sure the seige item doesn't get stuck
  1401. //in a wall or something
  1402. trace_t tr;
  1403. vec3_t TracePoint;
  1404. VectorCopy(targ->r.currentOrigin, TracePoint);
  1405. trap_Trace(&tr, targ->r.currentOrigin, ent->r.mins, ent->r.maxs,
  1406. targ->r.currentOrigin, targ->s.number, ent->clipmask);
  1407. if(tr.startsolid)
  1408. {//bad spawning area, try again with the trace up a bit.
  1409. TracePoint[2] += 30;
  1410. trap_Trace(&tr, TracePoint, ent->r.mins, ent->r.maxs, TracePoint,
  1411. ent->s.number, ent->clipmask);
  1412. if(tr.startsolid)
  1413. {//hmm, well that didn't work. try one last time with the item back
  1414. //away from where the dude was facing (in case the carrier was
  1415. //close to something they were attacking.)
  1416. vec3_t fwd;
  1417. if(targ->client)
  1418. {
  1419. AngleVectors(targ->client->ps.viewangles,fwd, NULL, NULL);
  1420. }
  1421. else
  1422. {
  1423. AngleVectors(targ->r.currentAngles,fwd, NULL, NULL);
  1424. }
  1425. VectorMA(TracePoint, -30, fwd, TracePoint);
  1426. trap_Trace(&tr, TracePoint, ent->r.mins, ent->r.maxs, TracePoint, ent->s.number, ent->clipmask);
  1427. if(tr.startsolid)
  1428. {//crap, that's all we got. just spawn at the defualt location.
  1429. return;
  1430. }
  1431. }
  1432. }
  1433. G_SetOrigin(ent, TracePoint);
  1434. //G_SetOrigin(ent, targ->r.currentOrigin);
  1435. trap_LinkEntity(ent);
  1436. }
  1437. }
  1438. }
  1439. /*QUAKED misc_siege_item (1 0 1) (-16 -16 -24) (16 16 32) ? x x STARTOFFRADAR
  1440. STARTOFFRADAR - start not displaying on radar, don't display until used.
  1441. "model" Name of model to use for the object
  1442. "mins" Actual mins of the object. Careful not to place it into a solid,
  1443. as these new mins will not be reflected visually in the editor.
  1444. Default value is "-16 -16 -24".
  1445. "maxs" Same as above for maxs. Default value is "16 16 32".
  1446. "targetname" If it has a targetname, it will only spawn upon being used.
  1447. "target2" Target to fire upon pickup. If none, nothing will happen.
  1448. "pickuponlyonce" If non-0, target2 will only be fired on the first pickup. If the item is
  1449. dropped and picked up again later, the target will not be fired off on
  1450. the sequential pickup. Default value is 1.
  1451. "target3" Target to fire upon delivery of the item to the goal point.
  1452. If none, nothing will happen. (but you should always want something to happen)
  1453. "health" If > 0, object can be damaged and will die once health reaches 0. Default is 0.
  1454. "showhealth" if health > 0, will show a health meter for this item
  1455. "teamowner" Which team owns this item, used only for deciding what color to make health meter
  1456. "target4" Target to fire upon death, if damageable. Default is none.
  1457. "deathfx" Effect to play on death, if damageable. Default is none.
  1458. "canpickup" If non-0, item can be picked up. Otherwise it will just be solid and sit on the
  1459. ground. Default is 1.
  1460. "pickupsound" Sound to play on pickup, if any.
  1461. "goaltarget" Must be the targetname of a trigger_multi/trigger_once. Once a player carrying
  1462. this object is brought inside the specified trigger, then that trigger will be
  1463. allowed to fire. Ideally it will target a siege objective or something like that.
  1464. "usephysics" If non-0, run standard physics on the object. Default is 1.
  1465. "mass" If usephysics, this will be the factored object mass. Default is 0.09.
  1466. "gravity" If usephysics, this will be the factored gravitational pull. Default is 3.0.
  1467. "bounce" If usephysics, this will be the factored bounce amount. Default is 1.3.
  1468. "teamnotouch" If 1 don't let team 1 pickup, if 2 don't let team 2. By default both teams
  1469. can pick this object up and carry it around until death.
  1470. "teamnocomplete" Same values as above, but controls if this object can be taken into the objective
  1471. area by said team.
  1472. "respawnfx" Plays this effect when respawning (e.g. it is left in an unknown area too long
  1473. and goes back to the original spot). If this is not specified there will be
  1474. no effect. (expected format is .efx file)
  1475. "paintarget" plop self on top of this guy's origin when we are used (only applies if the siege
  1476. item has a targetname)
  1477. "noradar" if non-0 this thing will not show up on radar
  1478. "forcelimit" if non-0, while carrying this item, the carrier's force powers will be crippled.
  1479. "target5" target to fire when respawning.
  1480. "target6" target to fire when dropped by someone carrying this item.
  1481. "icon" icon that represents the gametype item on the radar
  1482. health charge things only work with showhealth 1 on siege items that take damage.
  1483. "health_chargeamt" if non-0 will recharge this much health every...
  1484. "health_chargerate" ...this many milliseconds
  1485. */
  1486. void SP_misc_siege_item (gentity_t *ent)
  1487. {
  1488. int canpickup;
  1489. int noradar;
  1490. char *s;
  1491. if (!siege_valid || level.gametype != GT_SIEGE)
  1492. {
  1493. G_FreeEntity(ent);
  1494. return;
  1495. }
  1496. if (!ent->model || !ent->model[0])
  1497. {
  1498. G_Error("You must specify a model for misc_siege_item types.");
  1499. }
  1500. G_SpawnInt("canpickup", "1", &canpickup);
  1501. G_SpawnInt("usephysics", "1", &ent->genericValue1);
  1502. if (ent->genericValue1)
  1503. { //if we're using physics we want lerporigin smoothing
  1504. ent->s.eFlags |= EF_CLIENTSMOOTH;
  1505. }
  1506. G_SpawnInt("noradar", "0", &noradar);
  1507. //Want it to always show up as a goal object on radar
  1508. if (!noradar && !(ent->spawnflags & SIEGEITEM_STARTOFFRADAR))
  1509. {
  1510. ent->s.eFlags |= EF_RADAROBJECT;
  1511. }
  1512. //All clients want to know where it is at all times for radar
  1513. ent->r.svFlags |= SVF_BROADCAST;
  1514. G_SpawnInt("pickuponlyonce", "1", &ent->genericValue4);
  1515. G_SpawnInt("teamnotouch", "0", &ent->genericValue6);
  1516. G_SpawnInt("teamnocomplete", "0", &ent->genericValue7);
  1517. //Get default physics values.
  1518. G_SpawnFloat("mass", "0.09", &ent->mass);
  1519. G_SpawnFloat("gravity", "3.0", &ent->radius);
  1520. G_SpawnFloat("bounce", "1.3", &ent->random);
  1521. G_SpawnString( "pickupsound", "", &s );
  1522. if (s && s[0])
  1523. { //We have a pickup sound, so index it now.
  1524. ent->noise_index = G_SoundIndex(s);
  1525. }
  1526. G_SpawnString( "deathfx", "", &s );
  1527. if (s && s[0])
  1528. { //We have a death effect, so index it now.
  1529. ent->genericValue3 = G_EffectIndex(s);
  1530. }
  1531. G_SpawnString( "respawnfx", "", &s );
  1532. if (s && s[0])
  1533. { //We have a respawn effect, so index it now.
  1534. ent->genericValue10 = G_EffectIndex(s);
  1535. }
  1536. G_SpawnString( "icon", "", &s );
  1537. if (s && s[0])
  1538. {
  1539. // We have an icon, so index it now. We are reusing the genericenemyindex
  1540. // variable rather than adding a new one to the entity state.
  1541. ent->s.genericenemyindex = G_IconIndex(s);
  1542. }
  1543. ent->s.modelindex = G_ModelIndex(ent->model);
  1544. //Is the model a ghoul2 model?
  1545. if ( ent->model && !Q_stricmp( &ent->model[strlen(ent->model) - 4], ".glm" ) )
  1546. { //apparently so.
  1547. ent->s.modelGhoul2 = 1;
  1548. }
  1549. ent->s.eType = ET_GENERAL;
  1550. //Set the mins/maxs with default values.
  1551. G_SpawnVector("mins", "-16 -16 -24", ent->r.mins);
  1552. G_SpawnVector("maxs", "16 16 32", ent->r.maxs);
  1553. VectorCopy(ent->s.origin, ent->pos1); //store off the initial origin for respawning
  1554. G_SetOrigin(ent, ent->s.origin);
  1555. VectorCopy(ent->s.angles, ent->r.currentAngles);
  1556. VectorCopy(ent->s.angles, ent->s.apos.trBase);
  1557. G_SpawnInt("forcelimit", "0", &ent->genericValue15);
  1558. if (ent->health > 0)
  1559. { //If it has health, it can be killed.
  1560. int t;
  1561. ent->pain = SiegeItemPain;
  1562. ent->die = SiegeItemDie;
  1563. ent->takedamage = qtrue;
  1564. G_SpawnInt( "showhealth", "0", &t );
  1565. if (t)
  1566. { //a non-0 maxhealth value will mean we want to show the health on the hud
  1567. ent->maxHealth = ent->health;
  1568. G_ScaleNetHealth(ent);
  1569. G_SpawnInt( "health_chargeamt", "0", &ent->genericValue12);
  1570. G_SpawnInt( "health_chargerate", "0", &ent->genericValue13);
  1571. }
  1572. }
  1573. else
  1574. { //Otherwise no.
  1575. ent->takedamage = qfalse;
  1576. }
  1577. if (ent->spawnflags & SIEGEITEM_STARTOFFRADAR)
  1578. {
  1579. ent->use = SiegeItemUse;
  1580. }
  1581. else if (ent->targetname && ent->targetname[0])
  1582. {
  1583. ent->s.eFlags |= EF_NODRAW; //kind of hacky, but whatever
  1584. ent->genericValue11 = canpickup;
  1585. ent->use = SiegeItemUse;
  1586. ent->s.eFlags &= ~EF_RADAROBJECT;
  1587. }
  1588. if ( (!ent->targetname || !ent->targetname[0]) ||
  1589. (ent->spawnflags & SIEGEITEM_STARTOFFRADAR) )
  1590. {
  1591. if (canpickup || !ent->takedamage)
  1592. { //We want to be able to walk into it to pick it up then.
  1593. ent->r.contents = CONTENTS_TRIGGER;
  1594. ent->clipmask = CONTENTS_SOLID|CONTENTS_TERRAIN;
  1595. if (canpickup)
  1596. {
  1597. ent->touch = SiegeItemTouch;
  1598. }
  1599. }
  1600. else
  1601. { //Make it solid.
  1602. ent->r.contents = MASK_PLAYERSOLID;
  1603. ent->clipmask = MASK_PLAYERSOLID;
  1604. }
  1605. ent->think = SiegeItemThink;
  1606. ent->nextthink = level.time + FRAMETIME/2;
  1607. }
  1608. ent->genericValue8 = ENTITYNUM_NONE; //initialize the carrier to none
  1609. ent->neverFree = qtrue; //never free us unless we specifically request it.
  1610. trap_LinkEntity(ent);
  1611. }
  1612. //sends extra data about other client's in this client's PVS
  1613. //used for support guy etc.
  1614. //with this formatting:
  1615. //sxd 16,999,999,999|17,999,999,999
  1616. //assumed max 2 chars for cl num, 3 chars per ammo/health/maxhealth, even a single string full of
  1617. //info for all 32 clients should not get much past 450 bytes, which is well within a
  1618. //reasonable range. We don't need to send anything about the max ammo or current weapon, because
  1619. //currentState.weapon can be checked for the ent in question on the client. -rww
  1620. void G_SiegeClientExData(gentity_t *msgTarg)
  1621. {
  1622. gentity_t *ent;
  1623. int count = 0;
  1624. int i = 0;
  1625. char str[MAX_STRING_CHARS];
  1626. char scratch[MAX_STRING_CHARS];
  1627. while (i < level.num_entities && count < MAX_EXDATA_ENTS_TO_SEND)
  1628. {
  1629. ent = &g_entities[i];
  1630. if (ent->inuse && ent->client && msgTarg->s.number != ent->s.number &&
  1631. ent->s.eType == ET_PLAYER && msgTarg->client->sess.sessionTeam == ent->client->sess.sessionTeam &&
  1632. trap_InPVS(msgTarg->client->ps.origin, ent->client->ps.origin))
  1633. { //another client in the same pvs, send his jive
  1634. if (count)
  1635. { //append a seperating space if we are not the first in the list
  1636. Q_strcat(str, sizeof(str), " ");
  1637. }
  1638. else
  1639. { //otherwise create the prepended chunk
  1640. strcpy(str, "sxd ");
  1641. }
  1642. //append the stats
  1643. Com_sprintf(scratch, sizeof(scratch), "%i|%i|%i|%i", ent->s.number, ent->client->ps.stats[STAT_HEALTH],
  1644. ent->client->ps.stats[STAT_MAX_HEALTH], ent->client->ps.ammo[weaponData[ent->client->ps.weapon].ammoIndex]);
  1645. Q_strcat(str, sizeof(str), scratch);
  1646. count++;
  1647. }
  1648. i++;
  1649. }
  1650. if (!count)
  1651. { //nothing to send
  1652. return;
  1653. }
  1654. //send the string to him
  1655. trap_SendServerCommand(msgTarg-g_entities, str);
  1656. }