client.qc 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753
  1. /* Copyright (C) 1996-2022 id Software LLC
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation; either version 2 of the License, or
  5. (at your option) any later version.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program; if not, write to the Free Software
  12. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  13. See file, 'COPYING', for details.
  14. */
  15. // prototypes
  16. void () W_WeaponFrame;
  17. void() W_SetCurrentAmmo;
  18. void(entity attacker, float damage) player_pain;
  19. void() player_stand1;
  20. void (vector org) spawn_tfog;
  21. void (vector org, entity death_owner) spawn_tdeath;
  22. float modelindex_eyes, modelindex_player;
  23. /*
  24. =============================================================================
  25. LEVEL CHANGING / INTERMISSION
  26. =============================================================================
  27. */
  28. float intermission_running;
  29. float intermission_exittime;
  30. /*QUAKED info_intermission (1 0.5 0.5) (-16 -16 -16) (16 16 16)
  31. This is the camera point for the intermission.
  32. Use mangle instead of angle, so you can set pitch or roll as well as yaw. 'pitch roll yaw'
  33. */
  34. void() info_intermission =
  35. {
  36. };
  37. float reset_flag;
  38. void() SetChangeParms =
  39. {
  40. if(reset_flag)
  41. {
  42. setspawnparms(self);
  43. return;
  44. }
  45. if (self.health <= 0 || deathmatch)
  46. {
  47. SetNewParms ();
  48. return;
  49. }
  50. // remove items
  51. self.items = self.items - (self.items & (IT_KEY1 | IT_KEY2 | IT_INVISIBILITY | IT_INVULNERABILITY | IT_SUIT | IT_QUAD) );
  52. // cap super health
  53. if (self.health > self.max_health)
  54. self.health = self.max_health;
  55. if (self.health < self.max_health / 2)
  56. self.health = self.max_health / 2;
  57. parm1 = self.items;
  58. parm2 = self.health;
  59. parm3 = self.armorvalue;
  60. if (self.ammo_shells < 25)
  61. parm4 = 25;
  62. else
  63. parm4 = self.ammo_shells;
  64. parm5 = self.ammo_nails;
  65. parm6 = self.ammo_rockets;
  66. parm7 = self.ammo_cells;
  67. parm8 = self.weapon;
  68. parm9 = self.armortype * 100;
  69. };
  70. void() SetNewParms =
  71. {
  72. parm1 = IT_SHOTGUN | IT_AXE;
  73. if (skill == 3 && !deathmatch)
  74. parm2 = 50;
  75. else
  76. parm2 = 100;
  77. parm3 = 0;
  78. parm4 = 25;
  79. parm5 = 0;
  80. parm6 = 0;
  81. parm7 = 0;
  82. parm8 = 1;
  83. parm9 = 0;
  84. };
  85. void() DecodeLevelParms =
  86. {
  87. if (serverflags)
  88. {
  89. if (world.model == "maps/start.bsp")
  90. SetNewParms (); // take away all stuff on starting new episode
  91. }
  92. self.items = parm1;
  93. self.health = parm2;
  94. self.armorvalue = parm3;
  95. self.ammo_shells = parm4;
  96. self.ammo_nails = parm5;
  97. self.ammo_rockets = parm6;
  98. self.ammo_cells = parm7;
  99. self.weapon = parm8;
  100. self.armortype = parm9 * 0.01;
  101. };
  102. /*
  103. ============
  104. FindIntermission
  105. Returns the entity to view from
  106. ============
  107. */
  108. entity() FindIntermission =
  109. {
  110. local entity spot;
  111. local float cyc;
  112. // look for info_intermission first
  113. spot = find (world, classname, "info_intermission");
  114. if (spot)
  115. { // pick a random one
  116. cyc = random() * 4;
  117. while (cyc > 1)
  118. {
  119. spot = find (spot, classname, "info_intermission");
  120. if (!spot)
  121. spot = find (spot, classname, "info_intermission");
  122. cyc = cyc - 1;
  123. }
  124. return spot;
  125. }
  126. // then look for the start position
  127. spot = find (world, classname, "info_player_start");
  128. if (spot)
  129. return spot;
  130. // testinfo_player_start is only found in regioned levels
  131. spot = find (world, classname, "testplayerstart");
  132. if (spot)
  133. return spot;
  134. objerror ("FindIntermission: no spot");
  135. return world;
  136. };
  137. string nextmap;
  138. void() GotoNextMap =
  139. {
  140. if (cvar("samelevel")) // if samelevel is set, stay on same level
  141. changelevel (mapname);
  142. else
  143. changelevel (nextmap);
  144. };
  145. void() ExitIntermission =
  146. {
  147. // skip any text in deathmatch
  148. if (deathmatch)
  149. {
  150. GotoNextMap ();
  151. return;
  152. }
  153. intermission_exittime = time + 1;
  154. intermission_running = intermission_running + 1;
  155. //
  156. // run some text if at the end of an episode
  157. //
  158. if (intermission_running == 2)
  159. {
  160. if (world.model == "maps/e1m7.bsp")
  161. {
  162. WriteByte (MSG_ALL, SVC_CDTRACK);
  163. WriteByte (MSG_ALL, 2);
  164. WriteByte (MSG_ALL, 3);
  165. if (!cvar("registered"))
  166. {
  167. WriteByte (MSG_ALL, SVC_FINALE);
  168. WriteString (MSG_ALL, "$qc_finale_e1_shareware");
  169. }
  170. else
  171. {
  172. WriteByte (MSG_ALL, SVC_FINALE);
  173. WriteString (MSG_ALL, "$qc_finale_e1");
  174. }
  175. return;
  176. }
  177. else if (world.model == "maps/e2m6.bsp")
  178. {
  179. WriteByte (MSG_ALL, SVC_CDTRACK);
  180. WriteByte (MSG_ALL, 2);
  181. WriteByte (MSG_ALL, 3);
  182. WriteByte (MSG_ALL, SVC_FINALE);
  183. WriteString (MSG_ALL, "$qc_finale_e2");
  184. return;
  185. }
  186. else if (world.model == "maps/e3m6.bsp")
  187. {
  188. WriteByte (MSG_ALL, SVC_CDTRACK);
  189. WriteByte (MSG_ALL, 2);
  190. WriteByte (MSG_ALL, 3);
  191. WriteByte (MSG_ALL, SVC_FINALE);
  192. WriteString (MSG_ALL, "$qc_finale_e3");
  193. return;
  194. }
  195. else if (world.model == "maps/e4m7.bsp")
  196. {
  197. WriteByte (MSG_ALL, SVC_CDTRACK);
  198. WriteByte (MSG_ALL, 2);
  199. WriteByte (MSG_ALL, 3);
  200. WriteByte (MSG_ALL, SVC_FINALE);
  201. WriteString (MSG_ALL, "$qc_finale_e4");
  202. return;
  203. }
  204. GotoNextMap();
  205. }
  206. if (intermission_running == 3)
  207. {
  208. if (!cvar("registered"))
  209. { // shareware episode has been completed, go to sell screen
  210. WriteByte (MSG_ALL, SVC_SELLSCREEN);
  211. return;
  212. }
  213. if ( (serverflags&15) == 15)
  214. {
  215. WriteByte (MSG_ALL, SVC_FINALE);
  216. WriteString (MSG_ALL, "$qc_finale_all_runes");
  217. return;
  218. }
  219. }
  220. GotoNextMap();
  221. };
  222. /*
  223. ============
  224. IntermissionThink
  225. When the player presses attack or jump, change to the next level
  226. ============
  227. */
  228. void() IntermissionThink =
  229. {
  230. if (time < intermission_exittime)
  231. return;
  232. if (!self.button0 && !self.button1 && !self.button2)
  233. return;
  234. ExitIntermission ();
  235. };
  236. void() execute_changelevel =
  237. {
  238. local entity pos;
  239. intermission_running = 1;
  240. // enforce a wait time before allowing changelevel
  241. if (deathmatch)
  242. intermission_exittime = time + 5;
  243. else
  244. intermission_exittime = time + 2;
  245. WriteByte (MSG_ALL, SVC_CDTRACK);
  246. WriteByte (MSG_ALL, 3);
  247. WriteByte (MSG_ALL, 3);
  248. pos = FindIntermission ();
  249. other = find (world, classname, "player");
  250. while (other != world)
  251. {
  252. other.view_ofs = '0 0 0';
  253. other.angles = other.v_angle = pos.mangle;
  254. other.fixangle = TRUE; // turn this way immediately
  255. other.nextthink = time + 0.5;
  256. other.takedamage = DAMAGE_NO;
  257. other.solid = SOLID_NOT;
  258. other.movetype = MOVETYPE_NONE;
  259. other.modelindex = 0;
  260. setorigin (other, pos.origin);
  261. if (skill == 3)
  262. {
  263. // [NDS] haleyjd: achieve if completed E1M1 Nightmare Tyson style (axe only)
  264. if (other.fired_weapon == 0 && world.model == "maps/e1m1.bsp")
  265. {
  266. msg_entity = other;
  267. WriteByte (MSG_ONE, SVC_ACHIEVEMENT);
  268. WriteString(MSG_ONE, "ACH_PACIFIST"); // emphasis on "fist", I guess.
  269. }
  270. // [NDS] haleyjd: achieve if completed E4M6 Nightmare without taking damage
  271. if (other.took_damage == 0 && world.model == "maps/e4m6.bsp")
  272. {
  273. msg_entity = other;
  274. WriteByte (MSG_ONE, SVC_ACHIEVEMENT);
  275. WriteString(MSG_ONE, "ACH_PAINLESS_MAZE"); // emphasis on "fist", I guess.
  276. }
  277. }
  278. other = find (other, classname, "player");
  279. }
  280. WriteByte (MSG_ALL, SVC_INTERMISSION);
  281. if (campaign && world.model == "maps/e1m7.bsp")
  282. {
  283. WriteByte (MSG_ALL, SVC_ACHIEVEMENT);
  284. WriteString(MSG_ALL, "ACH_COMPLETE_E1M7");
  285. }
  286. else if (campaign && world.model == "maps/e2m6.bsp")
  287. {
  288. WriteByte (MSG_ALL, SVC_ACHIEVEMENT);
  289. WriteString(MSG_ALL, "ACH_COMPLETE_E2M6");
  290. }
  291. else if (campaign && world.model == "maps/e3m6.bsp")
  292. {
  293. WriteByte (MSG_ALL, SVC_ACHIEVEMENT);
  294. WriteString(MSG_ALL, "ACH_COMPLETE_E3M6");
  295. }
  296. else if (campaign && world.model == "maps/e4m7.bsp")
  297. {
  298. WriteByte (MSG_ALL, SVC_ACHIEVEMENT);
  299. WriteString(MSG_ALL, "ACH_COMPLETE_E4M7");
  300. }
  301. if (world.model == "maps/e1m4.bsp" && nextmap == "e1m8")
  302. {
  303. WriteByte (MSG_ALL, SVC_ACHIEVEMENT);
  304. WriteString(MSG_ALL, "ACH_FIND_E1M8");
  305. }
  306. else if (world.model == "maps/e2m3.bsp" && nextmap == "e2m7")
  307. {
  308. WriteByte (MSG_ALL, SVC_ACHIEVEMENT);
  309. WriteString(MSG_ALL, "ACH_FIND_E2M7");
  310. }
  311. else if (world.model == "maps/e3m4.bsp" && nextmap == "e3m7")
  312. {
  313. WriteByte (MSG_ALL, SVC_ACHIEVEMENT);
  314. WriteString(MSG_ALL, "ACH_FIND_E3M7");
  315. }
  316. else if (world.model == "maps/e4m5.bsp" && nextmap == "e4m8")
  317. {
  318. WriteByte (MSG_ALL, SVC_ACHIEVEMENT);
  319. WriteString(MSG_ALL, "ACH_FIND_E4M8");
  320. }
  321. };
  322. void() changelevel_touch =
  323. {
  324. if (other.classname != "player")
  325. return;
  326. if ((cvar("noexit") == 1) || ((cvar("noexit") == 2) && (mapname != "start")))
  327. {
  328. T_Damage (other, self, self, 50000);
  329. return;
  330. }
  331. if (coop || deathmatch)
  332. {
  333. bprint("$qc_exited", other.netname);
  334. }
  335. nextmap = self.map;
  336. SUB_UseTargets ();
  337. if ( (self.spawnflags & 1) && (deathmatch == 0) )
  338. { // NO_INTERMISSION
  339. GotoNextMap();
  340. return;
  341. }
  342. self.touch = SUB_Null;
  343. // we can't move people right now, because touch functions are called
  344. // in the middle of C movement code, so set a think time to do it
  345. self.think = execute_changelevel;
  346. self.nextthink = time + 0.1;
  347. };
  348. /*
  349. QUAKED trigger_changelevel (0.5 0.5 0.5) ? NO_INTERMISSION
  350. When the player touches this, he gets sent to the map listed in the "map" variable.
  351. Unless the NO_INTERMISSION flag is set, the view will go to the info_intermission spot and display stats.
  352. */
  353. void() trigger_changelevel =
  354. {
  355. if (!self.map)
  356. objerror ("changelevel trigger doesn't have map");
  357. self.netname = "changelevel";
  358. self.killstring = "$qc_ks_tried_leave";
  359. InitTrigger ();
  360. self.touch = changelevel_touch;
  361. };
  362. /*
  363. =============================================================================
  364. PLAYER GAME EDGE FUNCTIONS
  365. =============================================================================
  366. */
  367. void() set_suicide_frame;
  368. // called by ClientKill and DeadThink
  369. void() respawn =
  370. {
  371. if (coop)
  372. {
  373. // make a copy of the dead body for appearances sake
  374. CopyToBodyQueue (self);
  375. // get the spawn parms as they were at level start
  376. setspawnparms (self);
  377. // respawn
  378. PutClientInServer ();
  379. }
  380. else if (deathmatch)
  381. {
  382. // make a copy of the dead body for appearances sake
  383. CopyToBodyQueue (self);
  384. // set default spawn parms
  385. SetNewParms ();
  386. // respawn
  387. PutClientInServer ();
  388. }
  389. else
  390. {
  391. // restart the entire server
  392. // cvar_set("campaign", ftos(campaign));
  393. // localcmd ("restart\n");
  394. // put serverflags back to map's initial setting
  395. serverflags = startingserverflags;
  396. // request a reset to the parms
  397. reset_flag = TRUE;
  398. // "change" to the same level
  399. localcmd ("changelevel ");
  400. localcmd(mapname);
  401. localcmd("\n");
  402. }
  403. };
  404. /*
  405. ============
  406. ClientKill
  407. Player entered the suicide command
  408. ============
  409. */
  410. void() ClientKill =
  411. {
  412. bprint("$qc_suicides", self.netname);
  413. set_suicide_frame ();
  414. self.modelindex = modelindex_player;
  415. self.frags = self.frags - 2; // extra penalty
  416. respawn ();
  417. };
  418. /*
  419. ============
  420. PlayerVisibleToSpawnPoint
  421. Returns true if player can see this point
  422. ============
  423. */
  424. float PlayerVisibleToSpawnPoint( entity point ) {
  425. local vector spot1, spot2;
  426. local entity player = find( world, classname, "player" );
  427. while ( player ) {
  428. if ( player.health > 0 ) {
  429. spot1 = point.origin + player.view_ofs;
  430. spot2 = player.origin + player.view_ofs;
  431. traceline( spot1, spot2, TRUE, point );
  432. if ( trace_fraction >= 1.0f ) {
  433. return TRUE;
  434. }
  435. }
  436. player = find( player, classname, "player" );
  437. }
  438. return FALSE;
  439. }
  440. float IDEAL_DIST_FROM_DM_SPAWN_POINT = 384;
  441. float MIN_DIST_FROM_DM_SPAWN_POINT = 84;
  442. /*
  443. ============
  444. SelectSpawnPoint
  445. Returns the entity to spawn at
  446. ============
  447. */
  448. entity SelectSpawnPoint(float forceSpawn) {
  449. local entity spot, thing;
  450. local float numspots, totalspots;
  451. local float pcount;
  452. local entity spots;
  453. numspots = 0;
  454. totalspots = 0;
  455. // testinfo_player_start is only found in regioned levels
  456. spot = find( world, classname, "testplayerstart" );
  457. if ( spot )
  458. return spot;
  459. // choose a info_player_deathmatch point
  460. if ( coop ) {
  461. lastspawn = find( lastspawn, classname, "info_player_coop" );
  462. if ( lastspawn == world ) {
  463. lastspawn = find( lastspawn, classname, "info_player_start" );
  464. }
  465. if ( lastspawn != world ) {
  466. return lastspawn;
  467. }
  468. } else if ( deathmatch ) {
  469. // find all spots that don't have visible players nearby
  470. spots = world;
  471. spot = find( world, classname, "info_player_deathmatch" );
  472. while( spot ) {
  473. totalspots = totalspots + 1;
  474. thing = findradius( spot.origin, IDEAL_DIST_FROM_DM_SPAWN_POINT );
  475. pcount = 0;
  476. while( thing ) {
  477. if ( thing.classname == "player" && thing.health > 0 ) {
  478. pcount = pcount + 1;
  479. }
  480. thing = thing.chain;
  481. }
  482. if ( pcount == 0 ) {
  483. if ( PlayerVisibleToSpawnPoint( spot ) ) {
  484. pcount = pcount + 1;
  485. }
  486. }
  487. if ( pcount == 0 ) { // good spot!
  488. spot.goalentity = spots;
  489. spots = spot;
  490. numspots = numspots + 1;
  491. }
  492. // Get the next spot in the chain
  493. spot = find( spot, classname, "info_player_deathmatch" );
  494. }
  495. totalspots = totalspots - 1;
  496. // on small maps with few spawn points, our "ideal" spawn conditions may not be possible to meet
  497. // so fallback to just trying to pick a point without a player on top of it, so we don't start
  498. // a spawn frag loop
  499. if ( numspots == 0 ) {
  500. spot = find( world, classname, "info_player_deathmatch" );
  501. while( spot ) {
  502. thing = findradius( spot.origin, MIN_DIST_FROM_DM_SPAWN_POINT );
  503. pcount = 0;
  504. while( thing ) {
  505. if ( thing.classname == "player" && thing.health > 0 ) {
  506. pcount = pcount + 1;
  507. }
  508. thing = thing.chain;
  509. }
  510. if ( pcount == 0 ) { // good spot!
  511. spot.goalentity = spots;
  512. spots = spot;
  513. numspots = numspots + 1;
  514. }
  515. // Get the next spot in the chain
  516. spot = find( spot, classname, "info_player_deathmatch" );
  517. }
  518. }
  519. // uncomment to force a deferred spawn
  520. // if (forceSpawn == FALSE) return world;
  521. if ( !numspots ) {
  522. if (forceSpawn == FALSE) {
  523. return world;
  524. }
  525. // no spots available so just pick one at random
  526. totalspots = rint( ( random() * totalspots ) );
  527. spot = find( world, classname, "info_player_deathmatch" );
  528. while( totalspots > 0 ) {
  529. totalspots = totalspots - 1;
  530. spot = find( spot, classname, "info_player_deathmatch" );
  531. }
  532. return spot;
  533. }
  534. // Generate a random number between 1 and numspots
  535. numspots = numspots - 1;
  536. numspots = rint( ( random() * numspots ) );
  537. spot = spots;
  538. while( numspots > 0 ) {
  539. spot = spot.goalentity;
  540. numspots = numspots - 1;
  541. }
  542. return spot;
  543. }
  544. if ( serverflags ) { // return with a rune to start
  545. spot = find( world, classname, "info_player_start2" );
  546. if ( spot ) {
  547. return spot;
  548. }
  549. }
  550. spot = find( world, classname, "info_player_start" );
  551. if ( !spot ) {
  552. error( "PutClientInServer: no info_player_start on level" );
  553. }
  554. return spot;
  555. };
  556. /*
  557. ===========
  558. PutClientInServer
  559. called each time a player is spawned
  560. ============
  561. */
  562. void() DecodeLevelParms;
  563. void() PlayerDie;
  564. void() PutClientInServer =
  565. {
  566. local entity spot;
  567. self.classname = "player";
  568. if (skill == 3 && !deathmatch)
  569. self.health = 50;
  570. else
  571. self.health = 100;
  572. self.takedamage = DAMAGE_AIM;
  573. self.solid = SOLID_SLIDEBOX;
  574. self.movetype = MOVETYPE_WALK;
  575. self.show_hostile = 0;
  576. if (skill == 3 && !deathmatch)
  577. self.max_health = 50;
  578. else
  579. self.max_health = 100;
  580. self.flags = FL_CLIENT;
  581. self.air_finished = time + 12;
  582. self.dmg = 2; // initial water damage
  583. self.super_damage_finished = 0;
  584. self.radsuit_finished = 0;
  585. self.invisible_finished = 0;
  586. self.invincible_finished = 0;
  587. self.effects = 0;
  588. self.invincible_time = 0;
  589. self.healthrot_nextcheck = 0;
  590. self.fired_weapon = 0; // [NDS] haleyjd
  591. self.took_damage = 0; // [NDS] haleyjd
  592. self.team = TEAM_NONE;
  593. if ( coop ) {
  594. self.team = TEAM_HUMANS;
  595. }
  596. DecodeLevelParms ();
  597. W_SetCurrentAmmo ();
  598. self.attack_finished = time;
  599. self.th_pain = player_pain;
  600. self.th_die = PlayerDie;
  601. self.deadflag = DEAD_NO;
  602. // paustime is set by teleporters to keep the player from moving a while
  603. self.pausetime = 0;
  604. local float shouldTelefrag;
  605. if (self.spawn_deferred > 0 && time >= self.spawn_deferred) {
  606. dprint("forcing telefrag on this spawn\n");
  607. shouldTelefrag = TRUE;
  608. } else {
  609. shouldTelefrag = FALSE;
  610. }
  611. spot = SelectSpawnPoint(shouldTelefrag);
  612. if (spot == world) {
  613. self.takedamage = DAMAGE_NO;
  614. self.solid = SOLID_NOT;
  615. self.movetype = MOVETYPE_NONE;
  616. self.deadflag = DEAD_DEAD;
  617. setmodel(self, "");
  618. self.view_ofs = '0 0 1'; // not 0 because PlayerPreThink would return out
  619. self.velocity = '0 0 0';
  620. if (self.spawn_deferred == 0) {
  621. dprint("no spawns available! deferring\n");
  622. self.spawn_deferred = time + 5;
  623. }
  624. spot = FindIntermission();
  625. self.angles = self.v_angle = spot.mangle;
  626. self.fixangle = TRUE;
  627. self.origin = spot.origin;
  628. self.weaponmodel = "";
  629. self.weaponframe = 0;
  630. self.weapon = 0;
  631. return;
  632. }
  633. self.spawn_deferred = 0;
  634. self.origin = spot.origin + '0 0 1';
  635. self.angles = spot.angles;
  636. self.fixangle = TRUE; // turn this way immediately
  637. // oh, this is a hack!
  638. setmodel (self, "progs/eyes.mdl");
  639. modelindex_eyes = self.modelindex;
  640. setmodel (self, "progs/player.mdl");
  641. modelindex_player = self.modelindex;
  642. setsize (self, VEC_HULL_MIN, VEC_HULL_MAX);
  643. self.view_ofs = '0 0 22';
  644. self.velocity = '0 0 0'; // 1998-07-21 Player moves after respawn fix by Xian
  645. player_stand1 ();
  646. if (deathmatch || coop)
  647. {
  648. makevectorsfixed(self.angles);
  649. spawn_tfog (self.origin + v_forward*20);
  650. }
  651. spawn_tdeath (self.origin, self);
  652. stuffcmd(self, "-attack\n"); // prevent shooting after respawning
  653. };
  654. /*
  655. =============================================================================
  656. QUAKED FUNCTIONS
  657. =============================================================================
  658. */
  659. /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 24)
  660. The normal starting point for a level.
  661. */
  662. void() info_player_start =
  663. {
  664. };
  665. /*QUAKED info_player_start2 (1 0 0) (-16 -16 -24) (16 16 24)
  666. Only used on start map for the return point from an episode.
  667. */
  668. void() info_player_start2 =
  669. {
  670. };
  671. /*
  672. saved out by quaked in region mode
  673. */
  674. void() testplayerstart =
  675. {
  676. };
  677. /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 24)
  678. potential spawning position for deathmatch games
  679. */
  680. void() info_player_deathmatch =
  681. {
  682. };
  683. /*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 24)
  684. potential spawning position for coop games
  685. */
  686. void() info_player_coop =
  687. {
  688. };
  689. /*
  690. ===============================================================================
  691. RULES
  692. ===============================================================================
  693. */
  694. /*
  695. go to the next level for deathmatch
  696. */
  697. void() NextLevel =
  698. {
  699. local entity o;
  700. if (nextmap != string_null)
  701. return; // already done
  702. if (mapname == "start")
  703. {
  704. if (!cvar("registered"))
  705. {
  706. mapname = "e1m1";
  707. }
  708. else if (!(serverflags & 1))
  709. {
  710. mapname = "e1m1";
  711. serverflags = serverflags | 1;
  712. }
  713. else if (!(serverflags & 2))
  714. {
  715. mapname = "e2m1";
  716. serverflags = serverflags | 2;
  717. }
  718. else if (!(serverflags & 4))
  719. {
  720. mapname = "e3m1";
  721. serverflags = serverflags | 4;
  722. }
  723. else if (!(serverflags & 8))
  724. {
  725. mapname = "e4m1";
  726. serverflags = serverflags - 7;
  727. }
  728. o = spawn();
  729. o.map = mapname;
  730. }
  731. else
  732. {
  733. // find a trigger changelevel
  734. o = find(world, classname, "trigger_changelevel");
  735. if (!o || mapname == "start")
  736. { // go back to same map if no trigger_changelevel
  737. o = spawn();
  738. o.map = mapname;
  739. }
  740. }
  741. nextmap = o.map;
  742. gameover = TRUE;
  743. if (o.nextthink < time)
  744. {
  745. o.think = execute_changelevel;
  746. o.nextthink = time + 0.1;
  747. }
  748. };
  749. /*
  750. ============
  751. CheckRules
  752. Exit deathmatch games upon conditions
  753. ============
  754. */
  755. void() CheckRules =
  756. {
  757. local float timelimit;
  758. local float fraglimit;
  759. if (gameover) // someone else quit the game already
  760. return;
  761. timelimit = cvar("timelimit") * 60;
  762. fraglimit = cvar("fraglimit");
  763. if (timelimit && time >= timelimit)
  764. {
  765. NextLevel ();
  766. return;
  767. }
  768. if (fraglimit && self.frags >= fraglimit)
  769. {
  770. NextLevel ();
  771. return;
  772. }
  773. };
  774. //============================================================================
  775. void() PlayerDeathThink =
  776. {
  777. local float forward;
  778. if ((self.flags & FL_ONGROUND))
  779. {
  780. forward = vlen (self.velocity);
  781. forward = forward - 20;
  782. if (forward <= 0)
  783. self.velocity = '0 0 0';
  784. else
  785. self.velocity = forward * normalize(self.velocity);
  786. }
  787. if (self.spawn_deferred)
  788. {
  789. local entity spot;
  790. spot = SelectSpawnPoint(FALSE);
  791. //dprint("time {} >= self.spawn_deferred {}\n", ftos(time), ftos(self.spawn_deferred));
  792. if (spot != world || time >= self.spawn_deferred) {
  793. respawn();
  794. }
  795. return;
  796. }
  797. // wait for all buttons released
  798. if (self.deadflag == DEAD_DEAD)
  799. {
  800. if (self.button2 || self.button1 || self.button0)
  801. return;
  802. self.deadflag = DEAD_RESPAWNABLE;
  803. return;
  804. }
  805. // wait for any button down
  806. if (!self.button2 && !self.button1 && !self.button0)
  807. return;
  808. self.button0 = 0;
  809. self.button1 = 0;
  810. self.button2 = 0;
  811. respawn();
  812. };
  813. void() PlayerJump =
  814. {
  815. if (self.flags & FL_WATERJUMP)
  816. return;
  817. if (self.waterlevel >= 2)
  818. {
  819. if (self.watertype == CONTENT_WATER)
  820. self.velocity_z = 100;
  821. else if (self.watertype == CONTENT_SLIME)
  822. self.velocity_z = 80;
  823. else
  824. self.velocity_z = 50;
  825. // play swimming sound
  826. if (self.swim_flag < time)
  827. {
  828. self.swim_flag = time + 1;
  829. if (random() < 0.5)
  830. sound (self, CHAN_BODY, "misc/water1.wav", 1, ATTN_NORM);
  831. else
  832. sound (self, CHAN_BODY, "misc/water2.wav", 1, ATTN_NORM);
  833. }
  834. return;
  835. }
  836. if (!(self.flags & FL_ONGROUND))
  837. return;
  838. if ( !(self.flags & FL_JUMPRELEASED) )
  839. return; // don't pogo stick
  840. self.flags = self.flags - (self.flags & FL_JUMPRELEASED);
  841. self.flags = self.flags - FL_ONGROUND; // don't stairwalk
  842. self.button2 = 0;
  843. // player jumping sound
  844. sound (self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM);
  845. self.velocity_z = self.velocity_z + 270;
  846. };
  847. /*
  848. ===========
  849. WaterMove
  850. ============
  851. */
  852. .float dmgtime;
  853. void() WaterMove =
  854. {
  855. if (self.movetype == MOVETYPE_NOCLIP)
  856. return;
  857. if (self.health < 0)
  858. return;
  859. if (self.waterlevel != 3)
  860. {
  861. if (self.air_finished < time)
  862. sound (self, CHAN_VOICE, "player/gasp2.wav", 1, ATTN_NORM);
  863. else if (self.air_finished < time + 9)
  864. sound (self, CHAN_VOICE, "player/gasp1.wav", 1, ATTN_NORM);
  865. self.air_finished = time + 12;
  866. self.dmg = 2;
  867. }
  868. else if (self.air_finished < time)
  869. { // drown!
  870. if (self.pain_finished < time)
  871. {
  872. self.dmg = self.dmg + 2;
  873. if (self.dmg > 15)
  874. self.dmg = 10;
  875. T_Damage (self, world, world, self.dmg);
  876. self.pain_finished = time + 1;
  877. }
  878. }
  879. if (!self.waterlevel)
  880. {
  881. if (self.flags & FL_INWATER)
  882. {
  883. // play leave water sound
  884. sound (self, CHAN_BODY, "misc/outwater.wav", 1, ATTN_NORM);
  885. self.flags = self.flags - FL_INWATER;
  886. }
  887. return;
  888. }
  889. if (self.watertype == CONTENT_LAVA)
  890. { // do damage
  891. if (self.dmgtime < time)
  892. {
  893. if (self.radsuit_finished > time)
  894. self.dmgtime = time + 1;
  895. else
  896. self.dmgtime = time + 0.2;
  897. T_Damage (self, world, world, 10*self.waterlevel);
  898. }
  899. }
  900. else if (self.watertype == CONTENT_SLIME)
  901. { // do damage
  902. if (self.dmgtime < time && self.radsuit_finished < time)
  903. {
  904. self.dmgtime = time + 1;
  905. T_Damage (self, world, world, 4*self.waterlevel);
  906. }
  907. }
  908. if ( !(self.flags & FL_INWATER) )
  909. {
  910. // player enter water sound
  911. if (self.watertype == CONTENT_LAVA)
  912. sound (self, CHAN_BODY, "player/inlava.wav", 1, ATTN_NORM);
  913. if (self.watertype == CONTENT_WATER)
  914. sound (self, CHAN_BODY, "player/inh2o.wav", 1, ATTN_NORM);
  915. if (self.watertype == CONTENT_SLIME)
  916. sound (self, CHAN_BODY, "player/slimbrn2.wav", 1, ATTN_NORM);
  917. self.flags = self.flags + FL_INWATER;
  918. self.dmgtime = 0;
  919. }
  920. if (! (self.flags & FL_WATERJUMP) )
  921. self.velocity = self.velocity - 0.8*self.waterlevel*frametime*self.velocity;
  922. };
  923. void() CheckWaterJump =
  924. {
  925. local vector start, end;
  926. // check for a jump-out-of-water
  927. makevectorsfixed(self.angles);
  928. start = self.origin;
  929. start_z = start_z + 8;
  930. v_forward_z = 0;
  931. normalize(v_forward);
  932. end = start + v_forward*24;
  933. traceline (start, end, TRUE, self);
  934. if (trace_fraction < 1)
  935. { // solid at waist
  936. start_z = start_z + self.maxs_z - 8;
  937. end = start + v_forward*24;
  938. self.movedir = trace_plane_normal * -50;
  939. traceline (start, end, TRUE, self);
  940. if (trace_fraction == 1)
  941. { // open at eye level
  942. self.flags = self.flags | FL_WATERJUMP;
  943. self.velocity_z = 225;
  944. self.flags = self.flags - (self.flags & FL_JUMPRELEASED);
  945. self.teleport_time = time + 2; // safety net
  946. return;
  947. }
  948. }
  949. };
  950. /*
  951. ================
  952. PlayerPreThink
  953. Called every frame before physics are run
  954. ================
  955. */
  956. void() PlayerPreThink =
  957. {
  958. if (intermission_running)
  959. {
  960. IntermissionThink (); // otherwise a button could be missed between
  961. return; // the think tics
  962. }
  963. if (self.view_ofs == '0 0 0')
  964. return; // intermission or finale
  965. makevectors (self.v_angle); // is this still used
  966. if (deathmatch || coop)
  967. CheckRules ();
  968. WaterMove ();
  969. if (self.waterlevel == 2)
  970. CheckWaterJump ();
  971. if (self.deadflag >= DEAD_DEAD)
  972. {
  973. PlayerDeathThink ();
  974. return;
  975. }
  976. if (self.deadflag == DEAD_DYING)
  977. return; // dying, so do nothing
  978. if (self.button2)
  979. {
  980. PlayerJump ();
  981. }
  982. else
  983. self.flags = self.flags | FL_JUMPRELEASED;
  984. // teleporters can force a non-moving pause time
  985. if (time < self.pausetime)
  986. self.velocity = '0 0 0';
  987. if(time > self.attack_finished && self.currentammo == 0 && self.weapon != IT_AXE)
  988. {
  989. self.weapon = W_BestWeapon ();
  990. W_SetCurrentAmmo ();
  991. }
  992. };
  993. /*
  994. ================
  995. CheckPowerups
  996. Check for turning off powerups
  997. ================
  998. */
  999. void() CheckPowerups =
  1000. {
  1001. if (self.health <= 0)
  1002. return;
  1003. // invisibility
  1004. if (self.invisible_finished)
  1005. {
  1006. // sound and screen flash when items starts to run out
  1007. if (self.invisible_sound < time)
  1008. {
  1009. sound (self, CHAN_AUTO, "items/inv3.wav", 0.5, ATTN_IDLE);
  1010. self.invisible_sound = time + ((random() * 3) + 1);
  1011. }
  1012. if (self.invisible_finished < time + 3)
  1013. {
  1014. if (self.invisible_time == 1)
  1015. {
  1016. sprint(self, "$qc_ring_fade");
  1017. stuffcmd (self, "bf\n");
  1018. sound (self, CHAN_AUTO, "items/inv2.wav", 1, ATTN_NORM);
  1019. self.invisible_time = time + 1;
  1020. }
  1021. if (self.invisible_time < time)
  1022. {
  1023. self.invisible_time = time + 1;
  1024. stuffcmd (self, "bf\n");
  1025. }
  1026. }
  1027. if (self.invisible_finished < time)
  1028. { // just stopped
  1029. self.items = self.items - IT_INVISIBILITY;
  1030. self.invisible_finished = 0;
  1031. self.invisible_time = 0;
  1032. }
  1033. // use the eyes
  1034. self.frame = 0;
  1035. self.modelindex = modelindex_eyes;
  1036. }
  1037. else
  1038. self.modelindex = modelindex_player; // don't use eyes
  1039. // invincibility
  1040. if (self.invincible_finished)
  1041. {
  1042. // sound and screen flash when items starts to run out
  1043. if (self.invincible_finished < time + 3)
  1044. {
  1045. if (self.invincible_time == 1)
  1046. {
  1047. sprint(self, "$qc_protection_fade");
  1048. stuffcmd (self, "bf\n");
  1049. sound (self, CHAN_AUTO, "items/protect2.wav", 1, ATTN_NORM);
  1050. self.invincible_time = time + 1;
  1051. }
  1052. if (self.invincible_time < time)
  1053. {
  1054. self.invincible_time = time + 1;
  1055. stuffcmd (self, "bf\n");
  1056. }
  1057. }
  1058. if (self.invincible_finished < time)
  1059. { // just stopped
  1060. self.items = self.items - IT_INVULNERABILITY;
  1061. self.invincible_time = 0;
  1062. self.invincible_finished = 0;
  1063. }
  1064. if (self.invincible_finished > time)
  1065. self.effects = self.effects | EF_PENTALIGHT;
  1066. else
  1067. self.effects = self.effects - (self.effects & EF_PENTALIGHT);
  1068. }
  1069. // super damage
  1070. if (self.super_damage_finished)
  1071. {
  1072. // sound and screen flash when items starts to run out
  1073. if (self.super_damage_finished < time + 3)
  1074. {
  1075. if (self.super_time == 1)
  1076. {
  1077. sprint(self, "$qc_quad_fade");
  1078. stuffcmd (self, "bf\n");
  1079. sound (self, CHAN_AUTO, "items/damage2.wav", 1, ATTN_NORM);
  1080. self.super_time = time + 1;
  1081. }
  1082. if (self.super_time < time)
  1083. {
  1084. self.super_time = time + 1;
  1085. stuffcmd (self, "bf\n");
  1086. }
  1087. }
  1088. if (self.super_damage_finished < time)
  1089. { // just stopped
  1090. self.items = self.items - IT_QUAD;
  1091. self.super_damage_finished = 0;
  1092. self.super_time = 0;
  1093. }
  1094. if (self.super_damage_finished > time)
  1095. self.effects = self.effects | EF_QUADLIGHT;
  1096. else
  1097. self.effects = self.effects - (self.effects & EF_QUADLIGHT);
  1098. }
  1099. // suit
  1100. if (self.radsuit_finished)
  1101. {
  1102. self.air_finished = time + 12; // don't drown
  1103. // sound and screen flash when items starts to run out
  1104. if (self.radsuit_finished < time + 3)
  1105. {
  1106. if (self.rad_time == 1)
  1107. {
  1108. sprint(self, "$qc_biosuit_fade");
  1109. stuffcmd (self, "bf\n");
  1110. sound (self, CHAN_AUTO, "items/suit2.wav", 1, ATTN_NORM);
  1111. self.rad_time = time + 1;
  1112. }
  1113. if (self.rad_time < time)
  1114. {
  1115. self.rad_time = time + 1;
  1116. stuffcmd (self, "bf\n");
  1117. }
  1118. }
  1119. if (self.radsuit_finished < time)
  1120. { // just stopped
  1121. self.items = self.items - IT_SUIT;
  1122. self.rad_time = 0;
  1123. self.radsuit_finished = 0;
  1124. }
  1125. }
  1126. };
  1127. /*
  1128. ================
  1129. CheckHealthRot
  1130. Checks for taking off health points from a player
  1131. that recently used a Mega-Health.
  1132. ================
  1133. */
  1134. void CheckHealthRot() {
  1135. if ( !( self.items & IT_SUPERHEALTH ) ) {
  1136. return;
  1137. }
  1138. if ( self.healthrot_nextcheck > time ) {
  1139. return;
  1140. }
  1141. if ( self.health > self.max_health ) {
  1142. self.health = self.health - 1;
  1143. self.healthrot_nextcheck = time + 1;
  1144. return;
  1145. }
  1146. self.items = self.items - ( self.items & IT_SUPERHEALTH );
  1147. self.healthrot_nextcheck = 0;
  1148. }
  1149. /*
  1150. ================
  1151. PlayerPostThink
  1152. Called every frame after physics are run
  1153. ================
  1154. */
  1155. void() PlayerPostThink =
  1156. {
  1157. if ( self.view_ofs == '0 0 0') {
  1158. return; // intermission or finale
  1159. }
  1160. if ( self.deadflag ) {
  1161. return;
  1162. }
  1163. W_WeaponFrame (); // do weapon stuff
  1164. // check to see if player landed and play landing sound
  1165. if ((self.jump_flag < -300) && (self.flags & FL_ONGROUND) && (self.health > 0))
  1166. {
  1167. if (self.watertype == CONTENT_WATER)
  1168. sound (self, CHAN_BODY, "player/h2ojump.wav", 1, ATTN_NORM);
  1169. else if (self.jump_flag < -650)
  1170. {
  1171. T_Damage (self, world, world, 5);
  1172. sound (self, CHAN_VOICE, "player/land2.wav", 1, ATTN_NORM);
  1173. if (self.health <= 5)
  1174. self.deathtype = "falling";
  1175. }
  1176. else
  1177. sound (self, CHAN_VOICE, "player/land.wav", 1, ATTN_NORM);
  1178. self.jump_flag = 0;
  1179. }
  1180. if (!(self.flags & FL_ONGROUND))
  1181. self.jump_flag = self.velocity_z;
  1182. CheckPowerups();
  1183. CheckHealthRot();
  1184. };
  1185. /*
  1186. ===========
  1187. ClientConnect
  1188. called when a player connects to a server
  1189. ============
  1190. */
  1191. void() ClientConnect =
  1192. {
  1193. bprint("$qc_entered", self.netname);
  1194. // a client connecting during an intermission can cause problems
  1195. if (intermission_running)
  1196. ExitIntermission ();
  1197. };
  1198. /*
  1199. ===========
  1200. ClientDisconnect
  1201. called when a player disconnects from a server
  1202. ============
  1203. */
  1204. void() ClientDisconnect =
  1205. {
  1206. if (gameover)
  1207. return;
  1208. // if the level end trigger has been activated, just return
  1209. // since they aren't *really* leaving
  1210. // let everyone else know
  1211. bprint("$qc_left_game", self.netname, ftos(self.frags));
  1212. sound (self, CHAN_BODY, "player/tornoff2.wav", 1, ATTN_NONE);
  1213. self->effects = 0;
  1214. set_suicide_frame ();
  1215. };
  1216. /*
  1217. ===========
  1218. ClientObituary
  1219. called when a player dies
  1220. ============
  1221. */
  1222. void(entity targ, entity attacker) ClientObituary =
  1223. {
  1224. local float rnum;
  1225. // from GPL QW source
  1226. local float attackerteam, targteam;
  1227. attackerteam = attacker.team;
  1228. targteam = targ.team;
  1229. rnum = random();
  1230. if (targ.classname == "player")
  1231. {
  1232. if (attacker.classname == "teledeath")
  1233. {
  1234. bprint("$qc_telefragged", targ.netname, attacker.owner.netname);
  1235. attacker.owner.frags = attacker.owner.frags + 1;
  1236. return;
  1237. }
  1238. if (attacker.classname == "teledeath2")
  1239. {
  1240. bprint("$qc_satans_power", targ.netname);
  1241. targ.frags = targ.frags - 1;
  1242. return;
  1243. }
  1244. if (attacker.classname == "player")
  1245. {
  1246. if (targ == attacker)
  1247. {
  1248. // killed self
  1249. attacker.frags = attacker.frags - 1;
  1250. if (targ.weapon == 64 && targ.waterlevel > 1)
  1251. {
  1252. if (targ.watertype == CONTENT_SLIME)
  1253. bprint("$qc_discharge_slime", targ.netname);
  1254. else if (targ.watertype == CONTENT_LAVA)
  1255. bprint("$qc_discharge_lava", targ.netname);
  1256. else
  1257. bprint("$qc_discharge_water", targ.netname);
  1258. return;
  1259. }
  1260. if (targ.weapon == 16)
  1261. bprint("$qc_suicide_pin", targ.netname);
  1262. else if (rnum)
  1263. bprint("$qc_suicide_bored", targ.netname);
  1264. else
  1265. bprint("$qc_suicide_loaded", targ.netname);
  1266. return;
  1267. }
  1268. else if ( (teamplay == 2) && (targteam == attackerteam) &&
  1269. (attackerteam != 0) )
  1270. {
  1271. if (rnum < 0.25)
  1272. bprint("$qc_ff_teammate", attacker.netname);
  1273. else if (rnum < 0.50)
  1274. bprint("$qc_ff_glasses", attacker.netname);
  1275. else if (rnum < 0.75)
  1276. bprint("$qc_ff_otherteam", attacker.netname);
  1277. else
  1278. bprint("$qc_ff_friend", attacker.netname);
  1279. attacker.frags = attacker.frags - 1;
  1280. return;
  1281. }
  1282. else
  1283. {
  1284. attacker.frags = attacker.frags + 1;
  1285. rnum = attacker.weapon;
  1286. if (rnum == IT_AXE)
  1287. {
  1288. bprint("$qc_death_ax", targ.netname, attacker.netname);
  1289. return;
  1290. }
  1291. if (rnum == IT_SHOTGUN)
  1292. {
  1293. bprint("$qc_death_sg", targ.netname, attacker.netname);
  1294. return;
  1295. }
  1296. if (rnum == IT_SUPER_SHOTGUN)
  1297. {
  1298. bprint("$qc_death_dbl", targ.netname, attacker.netname);
  1299. return;
  1300. }
  1301. if (rnum == IT_NAILGUN)
  1302. {
  1303. bprint("$qc_death_nail", targ.netname, attacker.netname);
  1304. return;
  1305. }
  1306. if (rnum == IT_SUPER_NAILGUN)
  1307. {
  1308. bprint("$qc_death_sng", targ.netname, attacker.netname);
  1309. return;
  1310. }
  1311. if (rnum == IT_GRENADE_LAUNCHER)
  1312. {
  1313. if (targ.health < -40)
  1314. {
  1315. bprint("$qc_death_gl1", targ.netname, attacker.netname);
  1316. return;
  1317. }
  1318. else
  1319. {
  1320. bprint("$qc_death_gl2", targ.netname, attacker.netname);
  1321. return;
  1322. }
  1323. }
  1324. if (rnum == IT_ROCKET_LAUNCHER)
  1325. {
  1326. if (attacker.super_damage_finished > 0 && targ.health < -40)
  1327. {
  1328. rnum = random();
  1329. if (rnum < 0.3)
  1330. {
  1331. bprint("$qc_death_rl_quad1", targ.netname, attacker.netname);
  1332. return;
  1333. }
  1334. else if (rnum < 0.6)
  1335. {
  1336. bprint("$qc_death_rl_quad2", targ.netname, attacker.netname);
  1337. return;
  1338. }
  1339. else
  1340. {
  1341. bprint("$qc_death_rl1", targ.netname, attacker.netname);
  1342. return;
  1343. }
  1344. }
  1345. else
  1346. {
  1347. if (targ.health < -40)
  1348. {
  1349. bprint("$qc_death_rl2", targ.netname, attacker.netname);
  1350. return;
  1351. }
  1352. else
  1353. {
  1354. bprint("$qc_death_rl3", targ.netname, attacker.netname);
  1355. return;
  1356. }
  1357. }
  1358. }
  1359. if (rnum == IT_LIGHTNING)
  1360. {
  1361. if (attacker.waterlevel > 1)
  1362. {
  1363. bprint("$qc_death_lg1", targ.netname, attacker.netname);
  1364. if (attacker.invincible_finished)
  1365. {
  1366. msg_entity = attacker;
  1367. WriteByte (MSG_ONE, SVC_ACHIEVEMENT);
  1368. WriteString(MSG_ONE, "ACH_SURVIVE_DISCHARGE");
  1369. }
  1370. }
  1371. else
  1372. bprint("$qc_death_lg2", targ.netname, attacker.netname);
  1373. return;
  1374. }
  1375. }
  1376. return;
  1377. }
  1378. else
  1379. {
  1380. targ.frags = targ.frags - 1; // killed self
  1381. rnum = targ.watertype;
  1382. if (rnum == -3)
  1383. {
  1384. if (random() < 0.5)
  1385. bprint("$qc_death_drown1", targ.netname);
  1386. else
  1387. bprint("$qc_death_drown2", targ.netname);
  1388. return;
  1389. }
  1390. else if (rnum == -4)
  1391. {
  1392. if (random() < 0.5)
  1393. bprint("$qc_death_slime1", targ.netname);
  1394. else
  1395. bprint("$qc_death_slime2", targ.netname);
  1396. return;
  1397. }
  1398. else if (rnum == -5)
  1399. {
  1400. if (targ.health < -15)
  1401. {
  1402. bprint("$qc_death_lava1", targ.netname);
  1403. return;
  1404. }
  1405. if (random() < 0.5)
  1406. bprint("$qc_death_lava2", targ.netname);
  1407. else
  1408. bprint("$qc_death_lava3", targ.netname);
  1409. return;
  1410. }
  1411. if (attacker.solid == SOLID_BSP && attacker != world)
  1412. {
  1413. bprint("$qc_death_squish", targ.netname);
  1414. return;
  1415. }
  1416. if(attacker.killstring)
  1417. {
  1418. bprint(attacker.killstring, targ.netname);
  1419. return;
  1420. }
  1421. if (targ.deathtype == "falling")
  1422. {
  1423. targ.deathtype = string_null;
  1424. bprint("$qc_death_fall", targ.netname);
  1425. return;
  1426. }
  1427. bprint("$qc_death_died", targ.netname);
  1428. }
  1429. }
  1430. };