g_weapon.c 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. #include "g_local.h"
  4. /*
  5. =================
  6. check_dodge
  7. This is a support routine used when a client is firing
  8. a non-instant attack weapon. It checks to see if a
  9. monster's dodge function should be called.
  10. =================
  11. */
  12. static void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed)
  13. {
  14. vec3_t end;
  15. vec3_t v;
  16. trace_t tr;
  17. float eta;
  18. // easy mode only ducks one quarter the time
  19. if (skill->value == 0)
  20. {
  21. if (random() > 0.25)
  22. return;
  23. }
  24. VectorMA (start, 8192, dir, end);
  25. tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT);
  26. if ((tr.ent) && (tr.ent->svflags & SVF_MONSTER) && (tr.ent->health > 0) && (tr.ent->monsterinfo.dodge) && infront(tr.ent, self))
  27. {
  28. VectorSubtract (tr.endpos, start, v);
  29. eta = (VectorLength(v) - tr.ent->maxs[0]) / speed;
  30. tr.ent->monsterinfo.dodge (tr.ent, self, eta);
  31. }
  32. }
  33. /*
  34. =================
  35. fire_hit
  36. Used for all impact (hit/punch/slash) attacks
  37. =================
  38. */
  39. qboolean fire_hit (edict_t *self, vec3_t aim, int damage, int kick)
  40. {
  41. trace_t tr;
  42. vec3_t forward, right, up;
  43. vec3_t v;
  44. vec3_t point;
  45. float range;
  46. vec3_t dir;
  47. //see if enemy is in range
  48. VectorSubtract (self->enemy->s.origin, self->s.origin, dir);
  49. range = VectorLength(dir);
  50. if (range > aim[0])
  51. return false;
  52. if (aim[1] > self->mins[0] && aim[1] < self->maxs[0])
  53. {
  54. // the hit is straight on so back the range up to the edge of their bbox
  55. range -= self->enemy->maxs[0];
  56. }
  57. else
  58. {
  59. // this is a side hit so adjust the "right" value out to the edge of their bbox
  60. if (aim[1] < 0)
  61. aim[1] = self->enemy->mins[0];
  62. else
  63. aim[1] = self->enemy->maxs[0];
  64. }
  65. VectorMA (self->s.origin, range, dir, point);
  66. tr = gi.trace (self->s.origin, NULL, NULL, point, self, MASK_SHOT);
  67. if (tr.fraction < 1)
  68. {
  69. if (!tr.ent->takedamage)
  70. return false;
  71. // if it will hit any client/monster then hit the one we wanted to hit
  72. if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client))
  73. tr.ent = self->enemy;
  74. }
  75. AngleVectors(self->s.angles, forward, right, up);
  76. VectorMA (self->s.origin, range, forward, point);
  77. VectorMA (point, aim[1], right, point);
  78. VectorMA (point, aim[2], up, point);
  79. VectorSubtract (point, self->enemy->s.origin, dir);
  80. // do the damage
  81. T_Damage (tr.ent, self, self, dir, point, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, MOD_HIT);
  82. if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
  83. return false;
  84. // do our special form of knockback here
  85. VectorMA (self->enemy->absmin, 0.5, self->enemy->size, v);
  86. VectorSubtract (v, point, v);
  87. VectorNormalize (v);
  88. VectorMA (self->enemy->velocity, kick, v, self->enemy->velocity);
  89. if (self->enemy->velocity[2] > 0)
  90. self->enemy->groundentity = NULL;
  91. return true;
  92. }
  93. /*
  94. =================
  95. fire_lead
  96. This is an internal support routine used for bullet/pellet based weapons.
  97. =================
  98. */
  99. static void fire_lead (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod)
  100. {
  101. trace_t tr;
  102. vec3_t dir;
  103. vec3_t forward, right, up;
  104. vec3_t end;
  105. float r;
  106. float u;
  107. vec3_t water_start;
  108. qboolean water = false;
  109. int content_mask = MASK_SHOT | MASK_WATER;
  110. tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT);
  111. if (!(tr.fraction < 1.0))
  112. {
  113. vectoangles (aimdir, dir);
  114. AngleVectors (dir, forward, right, up);
  115. r = crandom()*hspread;
  116. u = crandom()*vspread;
  117. VectorMA (start, 8192, forward, end);
  118. VectorMA (end, r, right, end);
  119. VectorMA (end, u, up, end);
  120. if (gi.pointcontents (start) & MASK_WATER)
  121. {
  122. water = true;
  123. VectorCopy (start, water_start);
  124. content_mask &= ~MASK_WATER;
  125. }
  126. tr = gi.trace (start, NULL, NULL, end, self, content_mask);
  127. // see if we hit water
  128. if (tr.contents & MASK_WATER)
  129. {
  130. int color;
  131. water = true;
  132. VectorCopy (tr.endpos, water_start);
  133. if (!VectorCompare (start, tr.endpos))
  134. {
  135. if (tr.contents & CONTENTS_WATER)
  136. {
  137. if (strcmp(tr.surface->name, "*brwater") == 0)
  138. color = SPLASH_BROWN_WATER;
  139. else
  140. color = SPLASH_BLUE_WATER;
  141. }
  142. else if (tr.contents & CONTENTS_SLIME)
  143. color = SPLASH_SLIME;
  144. else if (tr.contents & CONTENTS_LAVA)
  145. color = SPLASH_LAVA;
  146. else
  147. color = SPLASH_UNKNOWN;
  148. if (color != SPLASH_UNKNOWN)
  149. {
  150. gi.WriteByte (svc_temp_entity);
  151. gi.WriteByte (TE_SPLASH);
  152. gi.WriteByte (8);
  153. gi.WritePosition (tr.endpos);
  154. gi.WriteDir (tr.plane.normal);
  155. gi.WriteByte (color);
  156. gi.multicast (tr.endpos, MULTICAST_PVS);
  157. }
  158. // change bullet's course when it enters water
  159. VectorSubtract (end, start, dir);
  160. vectoangles (dir, dir);
  161. AngleVectors (dir, forward, right, up);
  162. r = crandom()*hspread*2;
  163. u = crandom()*vspread*2;
  164. VectorMA (water_start, 8192, forward, end);
  165. VectorMA (end, r, right, end);
  166. VectorMA (end, u, up, end);
  167. }
  168. // re-trace ignoring water this time
  169. tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT);
  170. }
  171. }
  172. // send gun puff / flash
  173. if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
  174. {
  175. if (tr.fraction < 1.0)
  176. {
  177. if (tr.ent->takedamage)
  178. {
  179. T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod);
  180. }
  181. else
  182. {
  183. if (strncmp (tr.surface->name, "sky", 3) != 0)
  184. {
  185. gi.WriteByte (svc_temp_entity);
  186. gi.WriteByte (te_impact);
  187. gi.WritePosition (tr.endpos);
  188. gi.WriteDir (tr.plane.normal);
  189. gi.multicast (tr.endpos, MULTICAST_PVS);
  190. if (self->client)
  191. PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
  192. }
  193. }
  194. }
  195. }
  196. // if went through water, determine where the end and make a bubble trail
  197. if (water)
  198. {
  199. vec3_t pos;
  200. VectorSubtract (tr.endpos, water_start, dir);
  201. VectorNormalize (dir);
  202. VectorMA (tr.endpos, -2, dir, pos);
  203. if (gi.pointcontents (pos) & MASK_WATER)
  204. VectorCopy (pos, tr.endpos);
  205. else
  206. tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER);
  207. VectorAdd (water_start, tr.endpos, pos);
  208. VectorScale (pos, 0.5, pos);
  209. gi.WriteByte (svc_temp_entity);
  210. gi.WriteByte (TE_BUBBLETRAIL);
  211. gi.WritePosition (water_start);
  212. gi.WritePosition (tr.endpos);
  213. gi.multicast (pos, MULTICAST_PVS);
  214. }
  215. }
  216. /*
  217. =================
  218. fire_bullet
  219. Fires a single round. Used for machinegun and chaingun. Would be fine for
  220. pistols, rifles, etc....
  221. =================
  222. */
  223. void fire_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod)
  224. {
  225. fire_lead (self, start, aimdir, damage, kick, TE_GUNSHOT, hspread, vspread, mod);
  226. }
  227. /*
  228. =================
  229. fire_shotgun
  230. Shoots shotgun pellets. Used by shotgun and super shotgun.
  231. =================
  232. */
  233. void fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int mod)
  234. {
  235. int i;
  236. for (i = 0; i < count; i++)
  237. fire_lead (self, start, aimdir, damage, kick, TE_SHOTGUN, hspread, vspread, mod);
  238. }
  239. /*
  240. =================
  241. fire_blaster
  242. Fires a single blaster bolt. Used by the blaster and hyper blaster.
  243. =================
  244. */
  245. void blaster_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  246. {
  247. int mod;
  248. if (other == self->owner)
  249. return;
  250. if (surf && (surf->flags & SURF_SKY))
  251. {
  252. G_FreeEdict (self);
  253. return;
  254. }
  255. if (self->owner->client)
  256. PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
  257. if (other->takedamage)
  258. {
  259. if (self->spawnflags & 1)
  260. mod = MOD_HYPERBLASTER;
  261. else
  262. mod = MOD_BLASTER;
  263. T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod);
  264. }
  265. else
  266. {
  267. gi.WriteByte (svc_temp_entity);
  268. // RAFAEL
  269. //if (self->s.effects & TE_BLUEHYPERBLASTER)
  270. // gi.WriteByte (TE_BLUEHYPERBLASTER);
  271. //else
  272. gi.WriteByte (TE_BLASTER);
  273. gi.WritePosition (self->s.origin);
  274. if (!plane)
  275. gi.WriteDir (vec3_origin);
  276. else
  277. gi.WriteDir (plane->normal);
  278. gi.multicast (self->s.origin, MULTICAST_PVS);
  279. }
  280. G_FreeEdict (self);
  281. }
  282. void fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper)
  283. {
  284. edict_t *bolt;
  285. trace_t tr;
  286. VectorNormalize (dir);
  287. bolt = G_Spawn();
  288. bolt->svflags = SVF_DEADMONSTER;
  289. // yes, I know it looks weird that projectiles are deadmonsters
  290. // what this means is that when prediction is used against the object
  291. // (blaster/hyperblaster shots), the player won't be solid clipped against
  292. // the object. Right now trying to run into a firing hyperblaster
  293. // is very jerky since you are predicted 'against' the shots.
  294. VectorCopy (start, bolt->s.origin);
  295. VectorCopy (start, bolt->s.old_origin);
  296. vectoangles (dir, bolt->s.angles);
  297. VectorScale (dir, speed, bolt->velocity);
  298. bolt->movetype = MOVETYPE_FLYMISSILE;
  299. bolt->clipmask = MASK_SHOT;
  300. bolt->solid = SOLID_BBOX;
  301. bolt->s.effects |= effect;
  302. VectorClear (bolt->mins);
  303. VectorClear (bolt->maxs);
  304. bolt->s.modelindex = gi.modelindex ("models/objects/laser/tris.md2");
  305. bolt->s.sound = gi.soundindex ("misc/lasfly.wav");
  306. bolt->owner = self;
  307. bolt->touch = blaster_touch;
  308. bolt->nextthink = level.time + 2;
  309. bolt->think = G_FreeEdict;
  310. bolt->dmg = damage;
  311. bolt->classname = "bolt";
  312. if (hyper)
  313. bolt->spawnflags = 1;
  314. gi.linkentity (bolt);
  315. if (self->client)
  316. check_dodge (self, bolt->s.origin, dir, speed);
  317. tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
  318. if (tr.fraction < 1.0)
  319. {
  320. VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
  321. bolt->touch (bolt, tr.ent, NULL, NULL);
  322. }
  323. }
  324. // RAFAEL
  325. void fire_blueblaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect)
  326. {
  327. edict_t *bolt;
  328. trace_t tr;
  329. VectorNormalize (dir);
  330. bolt = G_Spawn ();
  331. VectorCopy (start, bolt->s.origin);
  332. VectorCopy (start, bolt->s.old_origin);
  333. vectoangles (dir, bolt->s.angles);
  334. VectorScale (dir, speed, bolt->velocity);
  335. bolt->movetype = MOVETYPE_FLYMISSILE;
  336. bolt->clipmask = MASK_SHOT;
  337. bolt->solid = SOLID_BBOX;
  338. bolt->s.effects |= effect;
  339. VectorClear (bolt->mins);
  340. VectorClear (bolt->maxs);
  341. bolt->s.modelindex = gi.modelindex ("models/objects/blaser/tris.md2");
  342. bolt->s.sound = gi.soundindex ("misc/lasfly.wav");
  343. bolt->owner = self;
  344. bolt->touch = blaster_touch;
  345. bolt->nextthink = level.time + 2;
  346. bolt->think = G_FreeEdict;
  347. bolt->dmg = damage;
  348. bolt->classname = "bolt";
  349. gi.linkentity (bolt);
  350. if (self->client)
  351. check_dodge (self, bolt->s.origin, dir, speed);
  352. tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
  353. if (tr.fraction < 1.0)
  354. {
  355. VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
  356. bolt->touch (bolt, tr.ent, NULL, NULL);
  357. }
  358. }
  359. /*
  360. =================
  361. fire_grenade
  362. =================
  363. */
  364. static void Grenade_Explode (edict_t *ent)
  365. {
  366. vec3_t origin;
  367. int mod;
  368. if (ent->owner->client)
  369. PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
  370. //FIXME: if we are onground then raise our Z just a bit since we are a point?
  371. if (ent->enemy)
  372. {
  373. float points;
  374. vec3_t v;
  375. vec3_t dir;
  376. VectorAdd (ent->enemy->mins, ent->enemy->maxs, v);
  377. VectorMA (ent->enemy->s.origin, 0.5, v, v);
  378. VectorSubtract (ent->s.origin, v, v);
  379. points = ent->dmg - 0.5 * VectorLength (v);
  380. VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir);
  381. if (ent->spawnflags & 1)
  382. mod = MOD_HANDGRENADE;
  383. else
  384. mod = MOD_GRENADE;
  385. T_Damage (ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
  386. }
  387. if (ent->spawnflags & 2)
  388. mod = MOD_HELD_GRENADE;
  389. else if (ent->spawnflags & 1)
  390. mod = MOD_HG_SPLASH;
  391. else
  392. mod = MOD_G_SPLASH;
  393. T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod);
  394. VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
  395. gi.WriteByte (svc_temp_entity);
  396. if (ent->waterlevel)
  397. {
  398. if (ent->groundentity)
  399. gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
  400. else
  401. gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
  402. }
  403. else
  404. {
  405. if (ent->groundentity)
  406. gi.WriteByte (TE_GRENADE_EXPLOSION);
  407. else
  408. gi.WriteByte (TE_ROCKET_EXPLOSION);
  409. }
  410. gi.WritePosition (origin);
  411. gi.multicast (ent->s.origin, MULTICAST_PHS);
  412. G_FreeEdict (ent);
  413. }
  414. static void Grenade_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  415. {
  416. if (other == ent->owner)
  417. return;
  418. if (surf && (surf->flags & SURF_SKY))
  419. {
  420. G_FreeEdict (ent);
  421. return;
  422. }
  423. if (!other->takedamage)
  424. {
  425. if (ent->spawnflags & 1)
  426. {
  427. if (random() > 0.5)
  428. gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
  429. else
  430. gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
  431. }
  432. else
  433. {
  434. gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
  435. }
  436. return;
  437. }
  438. ent->enemy = other;
  439. Grenade_Explode (ent);
  440. }
  441. void fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
  442. {
  443. edict_t *grenade;
  444. vec3_t dir;
  445. vec3_t forward, right, up;
  446. vectoangles (aimdir, dir);
  447. AngleVectors (dir, forward, right, up);
  448. grenade = G_Spawn();
  449. VectorCopy (start, grenade->s.origin);
  450. VectorScale (aimdir, speed, grenade->velocity);
  451. VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
  452. VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
  453. VectorSet (grenade->avelocity, 300, 300, 300);
  454. grenade->movetype = MOVETYPE_BOUNCE;
  455. grenade->clipmask = MASK_SHOT;
  456. grenade->solid = SOLID_BBOX;
  457. grenade->s.effects |= EF_GRENADE;
  458. VectorClear (grenade->mins);
  459. VectorClear (grenade->maxs);
  460. grenade->s.modelindex = gi.modelindex ("models/objects/grenade/tris.md2");
  461. grenade->owner = self;
  462. grenade->touch = Grenade_Touch;
  463. grenade->nextthink = level.time + timer;
  464. grenade->think = Grenade_Explode;
  465. grenade->dmg = damage;
  466. grenade->dmg_radius = damage_radius;
  467. grenade->classname = "grenade";
  468. gi.linkentity (grenade);
  469. }
  470. void fire_grenade2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held)
  471. {
  472. edict_t *grenade;
  473. vec3_t dir;
  474. vec3_t forward, right, up;
  475. vectoangles (aimdir, dir);
  476. AngleVectors (dir, forward, right, up);
  477. grenade = G_Spawn();
  478. VectorCopy (start, grenade->s.origin);
  479. VectorScale (aimdir, speed, grenade->velocity);
  480. VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
  481. VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
  482. VectorSet (grenade->avelocity, 300, 300, 300);
  483. grenade->movetype = MOVETYPE_BOUNCE;
  484. grenade->clipmask = MASK_SHOT;
  485. grenade->solid = SOLID_BBOX;
  486. grenade->s.effects |= EF_GRENADE;
  487. VectorClear (grenade->mins);
  488. VectorClear (grenade->maxs);
  489. grenade->s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2");
  490. grenade->owner = self;
  491. grenade->touch = Grenade_Touch;
  492. grenade->nextthink = level.time + timer;
  493. grenade->think = Grenade_Explode;
  494. grenade->dmg = damage;
  495. grenade->dmg_radius = damage_radius;
  496. grenade->classname = "hgrenade";
  497. if (held)
  498. grenade->spawnflags = 3;
  499. else
  500. grenade->spawnflags = 1;
  501. grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav");
  502. if (timer <= 0.0)
  503. Grenade_Explode (grenade);
  504. else
  505. {
  506. gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/hgrent1a.wav"), 1, ATTN_NORM, 0);
  507. gi.linkentity (grenade);
  508. }
  509. }
  510. /*
  511. =================
  512. fire_rocket
  513. =================
  514. */
  515. void rocket_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  516. {
  517. vec3_t origin;
  518. int n;
  519. if (other == ent->owner)
  520. return;
  521. if (surf && (surf->flags & SURF_SKY))
  522. {
  523. G_FreeEdict (ent);
  524. return;
  525. }
  526. if (ent->owner->client)
  527. PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
  528. // calculate position for the explosion entity
  529. VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
  530. if (other->takedamage)
  531. {
  532. T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET);
  533. }
  534. else
  535. {
  536. // don't throw any debris in net games
  537. if (!deathmatch->value && !coop->value)
  538. {
  539. if ((surf) && !(surf->flags & (SURF_WARP|SURF_TRANS33|SURF_TRANS66|SURF_FLOWING)))
  540. {
  541. n = rand() % 5;
  542. while(n--)
  543. ThrowDebris (ent, "models/objects/debris2/tris.md2", 2, ent->s.origin);
  544. }
  545. }
  546. }
  547. T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH);
  548. gi.WriteByte (svc_temp_entity);
  549. if (ent->waterlevel)
  550. gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
  551. else
  552. gi.WriteByte (TE_ROCKET_EXPLOSION);
  553. gi.WritePosition (origin);
  554. gi.multicast (ent->s.origin, MULTICAST_PHS);
  555. G_FreeEdict (ent);
  556. }
  557. void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
  558. {
  559. edict_t *rocket;
  560. rocket = G_Spawn();
  561. VectorCopy (start, rocket->s.origin);
  562. VectorCopy (dir, rocket->movedir);
  563. vectoangles (dir, rocket->s.angles);
  564. VectorScale (dir, speed, rocket->velocity);
  565. rocket->movetype = MOVETYPE_FLYMISSILE;
  566. rocket->clipmask = MASK_SHOT;
  567. rocket->solid = SOLID_BBOX;
  568. rocket->s.effects |= EF_ROCKET;
  569. VectorClear (rocket->mins);
  570. VectorClear (rocket->maxs);
  571. rocket->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2");
  572. rocket->owner = self;
  573. rocket->touch = rocket_touch;
  574. rocket->nextthink = level.time + 8000/speed;
  575. rocket->think = G_FreeEdict;
  576. rocket->dmg = damage;
  577. rocket->radius_dmg = radius_damage;
  578. rocket->dmg_radius = damage_radius;
  579. rocket->s.sound = gi.soundindex ("weapons/rockfly.wav");
  580. rocket->classname = "rocket";
  581. if (self->client)
  582. check_dodge (self, rocket->s.origin, dir, speed);
  583. gi.linkentity (rocket);
  584. }
  585. /*
  586. =================
  587. fire_rail
  588. =================
  589. */
  590. void fire_rail (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick)
  591. {
  592. vec3_t from;
  593. vec3_t end;
  594. trace_t tr;
  595. edict_t *ignore;
  596. int mask;
  597. qboolean water;
  598. VectorMA (start, 8192, aimdir, end);
  599. VectorCopy (start, from);
  600. ignore = self;
  601. water = false;
  602. mask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA;
  603. while (ignore)
  604. {
  605. tr = gi.trace (from, NULL, NULL, end, ignore, mask);
  606. if (tr.contents & (CONTENTS_SLIME|CONTENTS_LAVA))
  607. {
  608. mask &= ~(CONTENTS_SLIME|CONTENTS_LAVA);
  609. water = true;
  610. }
  611. else
  612. {
  613. //ZOID--added so rail goes through SOLID_BBOX entities (gibs, etc)
  614. if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client) ||
  615. (tr.ent->solid == SOLID_BBOX))
  616. ignore = tr.ent;
  617. else
  618. ignore = NULL;
  619. if ((tr.ent != self) && (tr.ent->takedamage))
  620. T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN);
  621. }
  622. VectorCopy (tr.endpos, from);
  623. }
  624. // send gun puff / flash
  625. gi.WriteByte (svc_temp_entity);
  626. gi.WriteByte (TE_RAILTRAIL);
  627. gi.WritePosition (start);
  628. gi.WritePosition (tr.endpos);
  629. gi.multicast (self->s.origin, MULTICAST_PHS);
  630. // gi.multicast (start, MULTICAST_PHS);
  631. if (water)
  632. {
  633. gi.WriteByte (svc_temp_entity);
  634. gi.WriteByte (TE_RAILTRAIL);
  635. gi.WritePosition (start);
  636. gi.WritePosition (tr.endpos);
  637. gi.multicast (tr.endpos, MULTICAST_PHS);
  638. }
  639. if (self->client)
  640. PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
  641. }
  642. /*
  643. =================
  644. fire_bfg
  645. =================
  646. */
  647. void bfg_explode (edict_t *self)
  648. {
  649. edict_t *ent;
  650. float points;
  651. vec3_t v;
  652. float dist;
  653. if (self->s.frame == 0)
  654. {
  655. // the BFG effect
  656. ent = NULL;
  657. while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL)
  658. {
  659. if (!ent->takedamage)
  660. continue;
  661. if (ent == self->owner)
  662. continue;
  663. if (!CanDamage (ent, self))
  664. continue;
  665. if (!CanDamage (ent, self->owner))
  666. continue;
  667. VectorAdd (ent->mins, ent->maxs, v);
  668. VectorMA (ent->s.origin, 0.5, v, v);
  669. VectorSubtract (self->s.origin, v, v);
  670. dist = VectorLength(v);
  671. points = self->radius_dmg * (1.0 - sqrt(dist/self->dmg_radius));
  672. if (ent == self->owner)
  673. points = points * 0.5;
  674. gi.WriteByte (svc_temp_entity);
  675. gi.WriteByte (TE_BFG_EXPLOSION);
  676. gi.WritePosition (ent->s.origin);
  677. gi.multicast (ent->s.origin, MULTICAST_PHS);
  678. T_Damage (ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT);
  679. }
  680. }
  681. self->nextthink = level.time + FRAMETIME;
  682. self->s.frame++;
  683. if (self->s.frame == 5)
  684. self->think = G_FreeEdict;
  685. }
  686. void bfg_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  687. {
  688. if (other == self->owner)
  689. return;
  690. if (surf && (surf->flags & SURF_SKY))
  691. {
  692. G_FreeEdict (self);
  693. return;
  694. }
  695. if (self->owner->client)
  696. PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
  697. // core explosion - prevents firing it into the wall/floor
  698. if (other->takedamage)
  699. T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST);
  700. T_RadiusDamage(self, self->owner, 200, other, 100, MOD_BFG_BLAST);
  701. gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0);
  702. self->solid = SOLID_NOT;
  703. self->touch = NULL;
  704. VectorMA (self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin);
  705. VectorClear (self->velocity);
  706. self->s.modelindex = gi.modelindex ("sprites/s_bfg3.sp2");
  707. self->s.frame = 0;
  708. self->s.sound = 0;
  709. self->s.effects &= ~EF_ANIM_ALLFAST;
  710. self->think = bfg_explode;
  711. self->nextthink = level.time + FRAMETIME;
  712. self->enemy = other;
  713. gi.WriteByte (svc_temp_entity);
  714. gi.WriteByte (TE_BFG_BIGEXPLOSION);
  715. gi.WritePosition (self->s.origin);
  716. gi.multicast (self->s.origin, MULTICAST_PVS);
  717. }
  718. void bfg_think (edict_t *self)
  719. {
  720. edict_t *ent;
  721. edict_t *ignore;
  722. vec3_t point;
  723. vec3_t dir;
  724. vec3_t start;
  725. vec3_t end;
  726. int dmg;
  727. trace_t tr;
  728. if (deathmatch->value)
  729. dmg = 5;
  730. else
  731. dmg = 10;
  732. ent = NULL;
  733. while ((ent = findradius(ent, self->s.origin, 256)) != NULL)
  734. {
  735. if (ent == self)
  736. continue;
  737. if (ent == self->owner)
  738. continue;
  739. if (!ent->takedamage)
  740. continue;
  741. if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0))
  742. continue;
  743. VectorMA (ent->absmin, 0.5, ent->size, point);
  744. VectorSubtract (point, self->s.origin, dir);
  745. VectorNormalize (dir);
  746. ignore = self;
  747. VectorCopy (self->s.origin, start);
  748. VectorMA (start, 2048, dir, end);
  749. while(1)
  750. {
  751. tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
  752. if (!tr.ent)
  753. break;
  754. // hurt it if we can
  755. if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner))
  756. T_Damage (tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER);
  757. // if we hit something that's not a monster or player we're done
  758. if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
  759. {
  760. gi.WriteByte (svc_temp_entity);
  761. gi.WriteByte (TE_LASER_SPARKS);
  762. gi.WriteByte (4);
  763. gi.WritePosition (tr.endpos);
  764. gi.WriteDir (tr.plane.normal);
  765. gi.WriteByte (self->s.skinnum);
  766. gi.multicast (tr.endpos, MULTICAST_PVS);
  767. break;
  768. }
  769. ignore = tr.ent;
  770. VectorCopy (tr.endpos, start);
  771. }
  772. gi.WriteByte (svc_temp_entity);
  773. gi.WriteByte (TE_BFG_LASER);
  774. gi.WritePosition (self->s.origin);
  775. gi.WritePosition (tr.endpos);
  776. gi.multicast (self->s.origin, MULTICAST_PHS);
  777. }
  778. self->nextthink = level.time + FRAMETIME;
  779. }
  780. void fire_bfg (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius)
  781. {
  782. edict_t *bfg;
  783. bfg = G_Spawn();
  784. VectorCopy (start, bfg->s.origin);
  785. VectorCopy (dir, bfg->movedir);
  786. vectoangles (dir, bfg->s.angles);
  787. VectorScale (dir, speed, bfg->velocity);
  788. bfg->movetype = MOVETYPE_FLYMISSILE;
  789. bfg->clipmask = MASK_SHOT;
  790. bfg->solid = SOLID_BBOX;
  791. bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST;
  792. VectorClear (bfg->mins);
  793. VectorClear (bfg->maxs);
  794. bfg->s.modelindex = gi.modelindex ("sprites/s_bfg1.sp2");
  795. bfg->owner = self;
  796. bfg->touch = bfg_touch;
  797. bfg->nextthink = level.time + 8000/speed;
  798. bfg->think = G_FreeEdict;
  799. bfg->radius_dmg = damage;
  800. bfg->dmg_radius = damage_radius;
  801. bfg->classname = "bfg blast";
  802. bfg->s.sound = gi.soundindex ("weapons/bfg__l1a.wav");
  803. bfg->think = bfg_think;
  804. bfg->nextthink = level.time + FRAMETIME;
  805. bfg->teammaster = bfg;
  806. bfg->teamchain = NULL;
  807. if (self->client)
  808. check_dodge (self, bfg->s.origin, dir, speed);
  809. gi.linkentity (bfg);
  810. }
  811. // RAFAEL
  812. /*
  813. fire_ionripper
  814. */
  815. void ionripper_sparks (edict_t *self)
  816. {
  817. gi.WriteByte (svc_temp_entity);
  818. gi.WriteByte (TE_WELDING_SPARKS);
  819. gi.WriteByte (0);
  820. gi.WritePosition (self->s.origin);
  821. gi.WriteDir (vec3_origin);
  822. gi.WriteByte (0xe4 + (rand()&3));
  823. gi.multicast (self->s.origin, MULTICAST_PVS);
  824. G_FreeEdict (self);
  825. }
  826. // RAFAEL
  827. void ionripper_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  828. {
  829. if (other == self->owner)
  830. return;
  831. if (surf && (surf->flags & SURF_SKY))
  832. {
  833. G_FreeEdict (self);
  834. return;
  835. }
  836. if (self->owner->client)
  837. PlayerNoise (self->owner, self->s.origin, PNOISE_IMPACT);
  838. if (other->takedamage)
  839. {
  840. T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, MOD_RIPPER);
  841. }
  842. else
  843. {
  844. return;
  845. }
  846. G_FreeEdict (self);
  847. }
  848. // RAFAEL
  849. void fire_ionripper (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect)
  850. {
  851. edict_t *ion;
  852. trace_t tr;
  853. VectorNormalize (dir);
  854. ion = G_Spawn ();
  855. VectorCopy (start, ion->s.origin);
  856. VectorCopy (start, ion->s.old_origin);
  857. vectoangles (dir, ion->s.angles);
  858. VectorScale (dir, speed, ion->velocity);
  859. ion->movetype = MOVETYPE_WALLBOUNCE;
  860. ion->clipmask = MASK_SHOT;
  861. ion->solid = SOLID_BBOX;
  862. ion->s.effects |= effect;
  863. ion->s.renderfx |= RF_FULLBRIGHT;
  864. VectorClear (ion->mins);
  865. VectorClear (ion->maxs);
  866. ion->s.modelindex = gi.modelindex ("models/objects/boomrang/tris.md2");
  867. ion->s.sound = gi.soundindex ("misc/lasfly.wav");
  868. ion->owner = self;
  869. ion->touch = ionripper_touch;
  870. ion->nextthink = level.time + 3;
  871. ion->think = ionripper_sparks;
  872. ion->dmg = damage;
  873. ion->dmg_radius = 100;
  874. gi.linkentity (ion);
  875. if (self->client)
  876. check_dodge (self, ion->s.origin, dir, speed);
  877. tr = gi.trace (self->s.origin, NULL, NULL, ion->s.origin, ion, MASK_SHOT);
  878. if (tr.fraction < 1.0)
  879. {
  880. VectorMA (ion->s.origin, -10, dir, ion->s.origin);
  881. ion->touch (ion, tr.ent, NULL, NULL);
  882. }
  883. }
  884. // RAFAEL
  885. /*
  886. fire_heat
  887. */
  888. void heat_think (edict_t *self)
  889. {
  890. edict_t *target = NULL;
  891. edict_t *aquire = NULL;
  892. vec3_t vec;
  893. vec3_t oldang;
  894. int len;
  895. int oldlen = 0;
  896. VectorClear (vec);
  897. // aquire new target
  898. while (( target = findradius (target, self->s.origin, 1024)) != NULL)
  899. {
  900. if (self->owner == target)
  901. continue;
  902. if (!target->svflags & SVF_MONSTER)
  903. continue;
  904. if (!target->client)
  905. continue;
  906. if (target->health <= 0)
  907. continue;
  908. if (!visible (self, target))
  909. continue;
  910. // if we need to reduce the tracking cone
  911. /*
  912. {
  913. vec3_t vec;
  914. float dot;
  915. vec3_t forward;
  916. AngleVectors (self->s.angles, forward, NULL, NULL);
  917. VectorSubtract (target->s.origin, self->s.origin, vec);
  918. VectorNormalize (vec);
  919. dot = DotProduct (vec, forward);
  920. if (dot > 0.6)
  921. continue;
  922. }
  923. */
  924. if (!infront (self, target))
  925. continue;
  926. VectorSubtract (self->s.origin, target->s.origin, vec);
  927. len = VectorLength (vec);
  928. if (aquire == NULL || len < oldlen)
  929. {
  930. aquire = target;
  931. self->target_ent = aquire;
  932. oldlen = len;
  933. }
  934. }
  935. if (aquire != NULL)
  936. {
  937. VectorCopy (self->s.angles, oldang);
  938. VectorSubtract (aquire->s.origin, self->s.origin, vec);
  939. vectoangles (vec, self->s.angles);
  940. VectorNormalize (vec);
  941. VectorCopy (vec, self->movedir);
  942. VectorScale (vec, 500, self->velocity);
  943. }
  944. self->nextthink = level.time + 0.1;
  945. }
  946. // RAFAEL
  947. void fire_heat (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
  948. {
  949. edict_t *heat;
  950. heat = G_Spawn();
  951. VectorCopy (start, heat->s.origin);
  952. VectorCopy (dir, heat->movedir);
  953. vectoangles (dir, heat->s.angles);
  954. VectorScale (dir, speed, heat->velocity);
  955. heat->movetype = MOVETYPE_FLYMISSILE;
  956. heat->clipmask = MASK_SHOT;
  957. heat->solid = SOLID_BBOX;
  958. heat->s.effects |= EF_ROCKET;
  959. VectorClear (heat->mins);
  960. VectorClear (heat->maxs);
  961. heat->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2");
  962. heat->owner = self;
  963. heat->touch = rocket_touch;
  964. heat->nextthink = level.time + 0.1;
  965. heat->think = heat_think;
  966. heat->dmg = damage;
  967. heat->radius_dmg = radius_damage;
  968. heat->dmg_radius = damage_radius;
  969. heat->s.sound = gi.soundindex ("weapons/rockfly.wav");
  970. if (self->client)
  971. check_dodge (self, heat->s.origin, dir, speed);
  972. gi.linkentity (heat);
  973. }
  974. // RAFAEL
  975. /*
  976. fire_plasma
  977. */
  978. void plasma_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  979. {
  980. vec3_t origin;
  981. if (other == ent->owner)
  982. return;
  983. if (surf && (surf->flags & SURF_SKY))
  984. {
  985. G_FreeEdict (ent);
  986. return;
  987. }
  988. if (ent->owner->client)
  989. PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
  990. // calculate position for the explosion entity
  991. VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
  992. if (other->takedamage)
  993. {
  994. T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_PHALANX);
  995. }
  996. T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_PHALANX);
  997. gi.WriteByte (svc_temp_entity);
  998. gi.WriteByte (TE_PLASMA_EXPLOSION);
  999. gi.WritePosition (origin);
  1000. gi.multicast (ent->s.origin, MULTICAST_PVS);
  1001. G_FreeEdict (ent);
  1002. }
  1003. // RAFAEL
  1004. void fire_plasma (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
  1005. {
  1006. edict_t *plasma;
  1007. plasma = G_Spawn();
  1008. VectorCopy (start, plasma->s.origin);
  1009. VectorCopy (dir, plasma->movedir);
  1010. vectoangles (dir, plasma->s.angles);
  1011. VectorScale (dir, speed, plasma->velocity);
  1012. plasma->movetype = MOVETYPE_FLYMISSILE;
  1013. plasma->clipmask = MASK_SHOT;
  1014. plasma->solid = SOLID_BBOX;
  1015. VectorClear (plasma->mins);
  1016. VectorClear (plasma->maxs);
  1017. plasma->owner = self;
  1018. plasma->touch = plasma_touch;
  1019. plasma->nextthink = level.time + 8000/speed;
  1020. plasma->think = G_FreeEdict;
  1021. plasma->dmg = damage;
  1022. plasma->radius_dmg = radius_damage;
  1023. plasma->dmg_radius = damage_radius;
  1024. plasma->s.sound = gi.soundindex ("weapons/rockfly.wav");
  1025. plasma->s.modelindex = gi.modelindex ("sprites/s_photon.sp2");
  1026. plasma->s.effects |= EF_PLASMA | EF_ANIM_ALLFAST;
  1027. if (self->client)
  1028. check_dodge (self, plasma->s.origin, dir, speed);
  1029. gi.linkentity (plasma);
  1030. }
  1031. // RAFAEL
  1032. extern void SP_item_foodcube (edict_t *best);
  1033. // RAFAEL
  1034. static void Trap_Think (edict_t *ent)
  1035. {
  1036. edict_t *target = NULL;
  1037. edict_t *best = NULL;
  1038. vec3_t vec;
  1039. int len, i;
  1040. int oldlen = 8000;
  1041. vec3_t forward, right, up;
  1042. if (ent->timestamp < level.time)
  1043. {
  1044. BecomeExplosion1(ent);
  1045. // note to self
  1046. // cause explosion damage???
  1047. return;
  1048. }
  1049. ent->nextthink = level.time + 0.1;
  1050. if (!ent->groundentity)
  1051. return;
  1052. // ok lets do the blood effect
  1053. if (ent->s.frame > 4)
  1054. {
  1055. if (ent->s.frame == 5)
  1056. {
  1057. if (ent->wait == 64)
  1058. gi.sound(ent, CHAN_VOICE, gi.soundindex ("weapons/trapdown.wav"), 1, ATTN_IDLE, 0);
  1059. ent->wait -= 2;
  1060. ent->delay += level.time;
  1061. for (i=0; i<3; i++)
  1062. {
  1063. best = G_Spawn();
  1064. if (strcmp (ent->enemy->classname, "monster_gekk") == 0)
  1065. {
  1066. best->s.modelindex = gi.modelindex ("models/objects/gekkgib/torso/tris.md2");
  1067. best->s.effects |= TE_GREENBLOOD;
  1068. }
  1069. else if (ent->mass > 200)
  1070. {
  1071. best->s.modelindex = gi.modelindex ("models/objects/gibs/chest/tris.md2");
  1072. best->s.effects |= TE_BLOOD;
  1073. }
  1074. else
  1075. {
  1076. best->s.modelindex = gi.modelindex ("models/objects/gibs/sm_meat/tris.md2");
  1077. best->s.effects |= TE_BLOOD;
  1078. }
  1079. AngleVectors (ent->s.angles, forward, right, up);
  1080. RotatePointAroundVector( vec, up, right, ((360.0/3)* i)+ent->delay);
  1081. VectorMA (vec, ent->wait/2, vec, vec);
  1082. VectorAdd(vec, ent->s.origin, vec);
  1083. VectorAdd(vec, forward, best->s.origin);
  1084. best->s.origin[2] = ent->s.origin[2] + ent->wait;
  1085. VectorCopy (ent->s.angles, best->s.angles);
  1086. best->solid = SOLID_NOT;
  1087. best->s.effects |= EF_GIB;
  1088. best->takedamage = DAMAGE_YES;
  1089. best->movetype = MOVETYPE_TOSS;
  1090. best->svflags |= SVF_MONSTER;
  1091. best->deadflag = DEAD_DEAD;
  1092. VectorClear (best->mins);
  1093. VectorClear (best->maxs);
  1094. best->watertype = gi.pointcontents(best->s.origin);
  1095. if (best->watertype & MASK_WATER)
  1096. best->waterlevel = 1;
  1097. best->nextthink = level.time + 0.1;
  1098. best->think = G_FreeEdict;
  1099. gi.linkentity (best);
  1100. }
  1101. if (ent->wait < 19)
  1102. ent->s.frame ++;
  1103. return;
  1104. }
  1105. ent->s.frame ++;
  1106. if (ent->s.frame == 8)
  1107. {
  1108. ent->nextthink = level.time + 1.0;
  1109. ent->think = G_FreeEdict;
  1110. best = G_Spawn ();
  1111. SP_item_foodcube (best);
  1112. VectorCopy (ent->s.origin, best->s.origin);
  1113. best->s.origin[2]+= 16;
  1114. best->velocity[2] = 400;
  1115. best->count = ent->mass;
  1116. gi.linkentity (best);
  1117. return;
  1118. }
  1119. return;
  1120. }
  1121. ent->s.effects &= ~EF_TRAP;
  1122. if (ent->s.frame >= 4)
  1123. {
  1124. ent->s.effects |= EF_TRAP;
  1125. VectorClear (ent->mins);
  1126. VectorClear (ent->maxs);
  1127. }
  1128. if (ent->s.frame < 4)
  1129. ent->s.frame++;
  1130. while ((target = findradius(target, ent->s.origin, 256)) != NULL)
  1131. {
  1132. if (target == ent)
  1133. continue;
  1134. if (!(target->svflags & SVF_MONSTER) && !target->client)
  1135. continue;
  1136. // if (target == ent->owner)
  1137. // continue;
  1138. if (target->health <= 0)
  1139. continue;
  1140. if (!visible (ent, target))
  1141. continue;
  1142. if (!best)
  1143. {
  1144. best = target;
  1145. continue;
  1146. }
  1147. VectorSubtract (ent->s.origin, target->s.origin, vec);
  1148. len = VectorLength (vec);
  1149. if (len < oldlen)
  1150. {
  1151. oldlen = len;
  1152. best = target;
  1153. }
  1154. }
  1155. // pull the enemy in
  1156. if (best)
  1157. {
  1158. vec3_t forward;
  1159. if (best->groundentity)
  1160. {
  1161. best->s.origin[2] += 1;
  1162. best->groundentity = NULL;
  1163. }
  1164. VectorSubtract (ent->s.origin, best->s.origin, vec);
  1165. len = VectorLength (vec);
  1166. if (best->client)
  1167. {
  1168. VectorNormalize (vec);
  1169. VectorMA (best->velocity, 250, vec, best->velocity);
  1170. }
  1171. else
  1172. {
  1173. best->ideal_yaw = vectoyaw(vec);
  1174. M_ChangeYaw (best);
  1175. AngleVectors (best->s.angles, forward, NULL, NULL);
  1176. VectorScale (forward, 256, best->velocity);
  1177. }
  1178. gi.sound(ent, CHAN_VOICE, gi.soundindex ("weapons/trapsuck.wav"), 1, ATTN_IDLE, 0);
  1179. if (len < 32)
  1180. {
  1181. if (best->mass < 400)
  1182. {
  1183. T_Damage (best, ent, ent->owner, vec3_origin, best->s.origin, vec3_origin, 100000, 1, 0, MOD_TRAP);
  1184. ent->enemy = best;
  1185. ent->wait = 64;
  1186. VectorCopy (ent->s.origin, ent->s.old_origin);
  1187. ent->timestamp = level.time + 30;
  1188. if (deathmatch->value)
  1189. ent->mass = best->mass/4;
  1190. else
  1191. ent->mass = best->mass/10;
  1192. // ok spawn the food cube
  1193. ent->s.frame = 5;
  1194. }
  1195. else
  1196. {
  1197. BecomeExplosion1(ent);
  1198. // note to self
  1199. // cause explosion damage???
  1200. return;
  1201. }
  1202. }
  1203. }
  1204. }
  1205. // RAFAEL
  1206. void fire_trap (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held)
  1207. {
  1208. edict_t *trap;
  1209. vec3_t dir;
  1210. vec3_t forward, right, up;
  1211. vectoangles (aimdir, dir);
  1212. AngleVectors (dir, forward, right, up);
  1213. trap = G_Spawn();
  1214. VectorCopy (start, trap->s.origin);
  1215. VectorScale (aimdir, speed, trap->velocity);
  1216. VectorMA (trap->velocity, 200 + crandom() * 10.0, up, trap->velocity);
  1217. VectorMA (trap->velocity, crandom() * 10.0, right, trap->velocity);
  1218. VectorSet (trap->avelocity, 0, 300, 0);
  1219. trap->movetype = MOVETYPE_BOUNCE;
  1220. trap->clipmask = MASK_SHOT;
  1221. trap->solid = SOLID_BBOX;
  1222. // VectorClear (trap->mins);
  1223. // VectorClear (trap->maxs);
  1224. VectorSet (trap->mins, -4, -4, 0);
  1225. VectorSet (trap->maxs, 4, 4, 8);
  1226. trap->s.modelindex = gi.modelindex ("models/weapons/z_trap/tris.md2");
  1227. trap->owner = self;
  1228. trap->nextthink = level.time + 1.0;
  1229. trap->think = Trap_Think;
  1230. trap->dmg = damage;
  1231. trap->dmg_radius = damage_radius;
  1232. trap->classname = "htrap";
  1233. // RAFAEL 16-APR-98
  1234. trap->s.sound = gi.soundindex ("weapons/traploop.wav");
  1235. // END 16-APR-98
  1236. if (held)
  1237. trap->spawnflags = 3;
  1238. else
  1239. trap->spawnflags = 1;
  1240. if (timer <= 0.0)
  1241. Grenade_Explode (trap);
  1242. else
  1243. {
  1244. // gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/trapdown.wav"), 1, ATTN_NORM, 0);
  1245. gi.linkentity (trap);
  1246. }
  1247. trap->timestamp = level.time + 30;
  1248. }