g_newweap.c 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. #include "g_local.h"
  4. #define INCLUDE_ETF_RIFLE 1
  5. #define INCLUDE_PROX 1
  6. //#define INCLUDE_FLAMETHROWER 1
  7. //#define INCLUDE_INCENDIARY 1
  8. #define INCLUDE_NUKE 1
  9. #define INCLUDE_MELEE 1
  10. #define INCLUDE_TESLA 1
  11. #define INCLUDE_BEAMS 1
  12. extern void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed);
  13. extern void hurt_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
  14. extern void droptofloor (edict_t *ent);
  15. extern void Grenade_Explode (edict_t *ent);
  16. extern void drawbbox (edict_t *ent);
  17. #ifdef INCLUDE_ETF_RIFLE
  18. /*
  19. ========================
  20. fire_flechette
  21. ========================
  22. */
  23. void flechette_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  24. {
  25. vec3_t dir;
  26. if (other == self->owner)
  27. return;
  28. if (surf && (surf->flags & SURF_SKY))
  29. {
  30. G_FreeEdict (self);
  31. return;
  32. }
  33. if (self->client)
  34. PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
  35. if (other->takedamage)
  36. {
  37. //gi.dprintf("t_damage %s\n", other->classname);
  38. T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
  39. self->dmg, self->dmg_radius, DAMAGE_NO_REG_ARMOR, MOD_ETF_RIFLE);
  40. }
  41. else
  42. {
  43. if(!plane)
  44. VectorClear (dir);
  45. else
  46. VectorScale (plane->normal, 256, dir);
  47. gi.WriteByte (svc_temp_entity);
  48. gi.WriteByte (TE_FLECHETTE);
  49. gi.WritePosition (self->s.origin);
  50. gi.WriteDir (dir);
  51. gi.multicast (self->s.origin, MULTICAST_PVS);
  52. // T_RadiusDamage(self, self->owner, 24, self, 48, MOD_ETF_RIFLE);
  53. }
  54. G_FreeEdict (self);
  55. }
  56. void fire_flechette (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int kick)
  57. {
  58. edict_t *flechette;
  59. VectorNormalize (dir);
  60. flechette = G_Spawn();
  61. VectorCopy (start, flechette->s.origin);
  62. VectorCopy (start, flechette->s.old_origin);
  63. vectoangles2 (dir, flechette->s.angles);
  64. VectorScale (dir, speed, flechette->velocity);
  65. flechette->movetype = MOVETYPE_FLYMISSILE;
  66. flechette->clipmask = MASK_SHOT;
  67. flechette->solid = SOLID_BBOX;
  68. flechette->s.renderfx = RF_FULLBRIGHT;
  69. VectorClear (flechette->mins);
  70. VectorClear (flechette->maxs);
  71. flechette->s.modelindex = gi.modelindex ("models/proj/flechette/tris.md2");
  72. // flechette->s.sound = gi.soundindex (""); // FIXME - correct sound!
  73. flechette->owner = self;
  74. flechette->touch = flechette_touch;
  75. flechette->nextthink = level.time + 8000/speed;
  76. flechette->think = G_FreeEdict;
  77. flechette->dmg = damage;
  78. flechette->dmg_radius = kick;
  79. gi.linkentity (flechette);
  80. if (self->client)
  81. check_dodge (self, flechette->s.origin, dir, speed);
  82. }
  83. #endif
  84. // **************************
  85. // PROX
  86. // **************************
  87. #ifdef INCLUDE_PROX
  88. #define PROX_TIME_TO_LIVE 45 // 45, 30, 15, 10
  89. #define PROX_TIME_DELAY 0.5
  90. #define PROX_BOUND_SIZE 96
  91. #define PROX_DAMAGE_RADIUS 192
  92. #define PROX_HEALTH 20
  93. #define PROX_DAMAGE 90
  94. //===============
  95. //===============
  96. void Prox_Explode (edict_t *ent)
  97. {
  98. vec3_t origin;
  99. edict_t *owner;
  100. // free the trigger field
  101. //PMM - changed teammaster to "mover" .. owner of the field is the prox
  102. if(ent->teamchain && ent->teamchain->owner == ent)
  103. G_FreeEdict(ent->teamchain);
  104. owner = ent;
  105. if(ent->teammaster)
  106. {
  107. owner = ent->teammaster;
  108. PlayerNoise(owner, ent->s.origin, PNOISE_IMPACT);
  109. }
  110. // play quad sound if appopriate
  111. if (ent->dmg > PROX_DAMAGE)
  112. gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
  113. ent->takedamage = DAMAGE_NO;
  114. T_RadiusDamage(ent, owner, ent->dmg, ent, PROX_DAMAGE_RADIUS, MOD_PROX);
  115. VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
  116. gi.WriteByte (svc_temp_entity);
  117. if (ent->groundentity)
  118. gi.WriteByte (TE_GRENADE_EXPLOSION);
  119. else
  120. gi.WriteByte (TE_ROCKET_EXPLOSION);
  121. gi.WritePosition (origin);
  122. gi.multicast (ent->s.origin, MULTICAST_PVS);
  123. G_FreeEdict (ent);
  124. }
  125. //===============
  126. //===============
  127. void prox_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  128. {
  129. // gi.dprintf("prox_die\n");
  130. // if set off by another prox, delay a little (chained explosions)
  131. if (strcmp(inflictor->classname, "prox"))
  132. {
  133. self->takedamage = DAMAGE_NO;
  134. Prox_Explode(self);
  135. }
  136. else
  137. {
  138. self->takedamage = DAMAGE_NO;
  139. self->think = Prox_Explode;
  140. self->nextthink = level.time + FRAMETIME;
  141. }
  142. }
  143. //===============
  144. //===============
  145. void Prox_Field_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  146. {
  147. edict_t *prox;
  148. if (!(other->svflags & SVF_MONSTER) && !other->client)
  149. return;
  150. // trigger the prox mine if it's still there, and still mine.
  151. prox = ent->owner;
  152. if (other == prox) // don't set self off
  153. return;
  154. if (prox->think == Prox_Explode) // we're set to blow!
  155. {
  156. // if ((g_showlogic) && (g_showlogic->value))
  157. // gi.dprintf ("%f - prox already gone off!\n", level.time);
  158. return;
  159. }
  160. if(prox->teamchain == ent)
  161. {
  162. gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/proxwarn.wav"), 1, ATTN_NORM, 0);
  163. prox->think = Prox_Explode;
  164. prox->nextthink = level.time + PROX_TIME_DELAY;
  165. return;
  166. }
  167. ent->solid = SOLID_NOT;
  168. G_FreeEdict(ent);
  169. }
  170. //===============
  171. //===============
  172. void prox_seek (edict_t *ent)
  173. {
  174. if(level.time > ent->wait)
  175. {
  176. Prox_Explode(ent);
  177. }
  178. else
  179. {
  180. ent->s.frame++;
  181. if(ent->s.frame > 13)
  182. ent->s.frame = 9;
  183. ent->think = prox_seek;
  184. ent->nextthink = level.time + 0.1;
  185. }
  186. }
  187. //===============
  188. //===============
  189. void prox_open (edict_t *ent)
  190. {
  191. edict_t *search;
  192. search = NULL;
  193. // gi.dprintf("prox_open %d\n", ent->s.frame);
  194. // gi.dprintf("%f\n", ent->velocity[2]);
  195. if(ent->s.frame == 9) // end of opening animation
  196. {
  197. // set the owner to NULL so the owner can shoot it, etc. needs to be done here so the owner
  198. // doesn't get stuck on it while it's opening if fired at point blank wall
  199. ent->s.sound = 0;
  200. ent->owner = NULL;
  201. if(ent->teamchain)
  202. ent->teamchain->touch = Prox_Field_Touch;
  203. while ((search = findradius(search, ent->s.origin, PROX_DAMAGE_RADIUS+10)) != NULL)
  204. {
  205. if (!search->classname) // tag token and other weird shit
  206. continue;
  207. // if (!search->takedamage)
  208. // continue;
  209. // if it's a monster or player with health > 0
  210. // or it's a player start point
  211. // and we can see it
  212. // blow up
  213. if (
  214. (
  215. (((search->svflags & SVF_MONSTER) || (search->client)) && (search->health > 0)) ||
  216. (
  217. (deathmatch->value) &&
  218. (
  219. (!strcmp(search->classname, "info_player_deathmatch")) ||
  220. (!strcmp(search->classname, "info_player_start")) ||
  221. (!strcmp(search->classname, "info_player_coop")) ||
  222. (!strcmp(search->classname, "misc_teleporter_dest"))
  223. )
  224. )
  225. )
  226. && (visible (search, ent))
  227. )
  228. {
  229. gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/proxwarn.wav"), 1, ATTN_NORM, 0);
  230. Prox_Explode (ent);
  231. return;
  232. }
  233. }
  234. if (strong_mines && (strong_mines->value))
  235. ent->wait = level.time + PROX_TIME_TO_LIVE;
  236. else
  237. {
  238. switch (ent->dmg/PROX_DAMAGE)
  239. {
  240. case 1:
  241. ent->wait = level.time + PROX_TIME_TO_LIVE;
  242. break;
  243. case 2:
  244. ent->wait = level.time + 30;
  245. break;
  246. case 4:
  247. ent->wait = level.time + 15;
  248. break;
  249. case 8:
  250. ent->wait = level.time + 10;
  251. break;
  252. default:
  253. // if ((g_showlogic) && (g_showlogic->value))
  254. // gi.dprintf ("prox with unknown multiplier %d!\n", ent->dmg/PROX_DAMAGE);
  255. ent->wait = level.time + PROX_TIME_TO_LIVE;
  256. break;
  257. }
  258. }
  259. // ent->wait = level.time + PROX_TIME_TO_LIVE;
  260. ent->think = prox_seek;
  261. ent->nextthink = level.time + 0.2;
  262. }
  263. else
  264. {
  265. if (ent->s.frame == 0)
  266. gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/proxopen.wav"), 1, ATTN_NORM, 0);
  267. //ent->s.sound = gi.soundindex ("weapons/proxopen.wav");
  268. ent->s.frame++;
  269. ent->think = prox_open;
  270. ent->nextthink = level.time + 0.05;
  271. }
  272. }
  273. //===============
  274. //===============
  275. void prox_land (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  276. {
  277. edict_t *field;
  278. vec3_t dir;
  279. vec3_t forward, right, up;
  280. int makeslave = 0;
  281. int movetype = MOVETYPE_NONE;
  282. int stick_ok = 0;
  283. vec3_t land_point;
  284. // must turn off owner so owner can shoot it and set it off
  285. // moved to prox_open so owner can get away from it if fired at pointblank range into
  286. // wall
  287. // ent->owner = NULL;
  288. // if ((g_showlogic) && (g_showlogic->value))
  289. // gi.dprintf ("land - %2.2f %2.2f %2.2f\n", ent->velocity[0], ent->velocity[1], ent->velocity[2]);
  290. if (surf && (surf->flags & SURF_SKY))
  291. {
  292. G_FreeEdict(ent);
  293. return;
  294. }
  295. if (plane->normal)
  296. {
  297. VectorMA (ent->s.origin, -10.0, plane->normal, land_point);
  298. if (gi.pointcontents (land_point) & (CONTENTS_SLIME|CONTENTS_LAVA))
  299. {
  300. Prox_Explode (ent);
  301. return;
  302. }
  303. }
  304. if ((other->svflags & SVF_MONSTER) || other->client || (other->svflags & SVF_DAMAGEABLE))
  305. {
  306. if(other != ent->teammaster)
  307. Prox_Explode(ent);
  308. return;
  309. }
  310. #define STOP_EPSILON 0.1
  311. else if (other != world)
  312. {
  313. //Here we need to check to see if we can stop on this entity.
  314. //Note that plane can be NULL
  315. //PMM - code stolen from g_phys (ClipVelocity)
  316. vec3_t out;
  317. float backoff, change;
  318. int i;
  319. if (!plane->normal) // this happens if you hit a point object, maybe other cases
  320. {
  321. // Since we can't tell what's going to happen, just blow up
  322. // if ((g_showlogic) && (g_showlogic->value))
  323. // gi.dprintf ("bad normal for surface, exploding!\n");
  324. Prox_Explode(ent);
  325. return;
  326. }
  327. if ((other->movetype == MOVETYPE_PUSH) && (plane->normal[2] > 0.7))
  328. stick_ok = 1;
  329. else
  330. stick_ok = 0;
  331. backoff = DotProduct (ent->velocity, plane->normal) * 1.5;
  332. for (i=0 ; i<3 ; i++)
  333. {
  334. change = plane->normal[i]*backoff;
  335. out[i] = ent->velocity[i] - change;
  336. if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
  337. out[i] = 0;
  338. }
  339. if (out[2] > 60)
  340. return;
  341. movetype = MOVETYPE_BOUNCE;
  342. // if we're here, we're going to stop on an entity
  343. if (stick_ok)
  344. { // it's a happy entity
  345. VectorCopy (vec3_origin, ent->velocity);
  346. VectorCopy (vec3_origin, ent->avelocity);
  347. }
  348. else // no-stick. teflon time
  349. {
  350. if (plane->normal[2] > 0.7)
  351. {
  352. // if ((g_showlogic) && (g_showlogic->value))
  353. // gi.dprintf ("stuck on entity, blowing up!\n");
  354. Prox_Explode(ent);
  355. return;
  356. }
  357. return;
  358. }
  359. }
  360. else if (other->s.modelindex != 1)
  361. return;
  362. vectoangles2 (plane->normal, dir);
  363. AngleVectors (dir, forward, right, up);
  364. if (gi.pointcontents (ent->s.origin) & (CONTENTS_LAVA|CONTENTS_SLIME))
  365. {
  366. Prox_Explode (ent);
  367. return;
  368. }
  369. field = G_Spawn();
  370. VectorCopy (ent->s.origin, field->s.origin);
  371. VectorClear(field->velocity);
  372. VectorClear(field->avelocity);
  373. VectorSet(field->mins, -PROX_BOUND_SIZE, -PROX_BOUND_SIZE, -PROX_BOUND_SIZE);
  374. VectorSet(field->maxs, PROX_BOUND_SIZE, PROX_BOUND_SIZE, PROX_BOUND_SIZE);
  375. field->movetype = MOVETYPE_NONE;
  376. field->solid = SOLID_TRIGGER;
  377. field->owner = ent;
  378. field->classname = "prox_field";
  379. field->teammaster = ent;
  380. gi.linkentity (field);
  381. VectorClear(ent->velocity);
  382. VectorClear(ent->avelocity);
  383. // rotate to vertical
  384. dir[PITCH] = dir[PITCH] + 90;
  385. VectorCopy (dir, ent->s.angles);
  386. ent->takedamage = DAMAGE_AIM;
  387. ent->movetype = movetype; // either bounce or none, depending on whether we stuck to something
  388. ent->die = prox_die;
  389. ent->teamchain = field;
  390. ent->health = PROX_HEALTH;
  391. ent->nextthink = level.time + 0.05;
  392. ent->think = prox_open;
  393. ent->touch = NULL;
  394. ent->solid = SOLID_BBOX;
  395. // record who we're attached to
  396. // ent->teammaster = other;
  397. gi.linkentity(ent);
  398. }
  399. //===============
  400. //===============
  401. void fire_prox (edict_t *self, vec3_t start, vec3_t aimdir, int damage_multiplier, int speed)
  402. {
  403. edict_t *prox;
  404. vec3_t dir;
  405. vec3_t forward, right, up;
  406. vectoangles2 (aimdir, dir);
  407. AngleVectors (dir, forward, right, up);
  408. // if ((g_showlogic) && (g_showlogic->value))
  409. // gi.dprintf ("start %s aim %s speed %d\n", vtos(start), vtos(aimdir), speed);
  410. prox = G_Spawn();
  411. VectorCopy (start, prox->s.origin);
  412. VectorScale (aimdir, speed, prox->velocity);
  413. VectorMA (prox->velocity, 200 + crandom() * 10.0, up, prox->velocity);
  414. VectorMA (prox->velocity, crandom() * 10.0, right, prox->velocity);
  415. VectorCopy (dir, prox->s.angles);
  416. prox->s.angles[PITCH]-=90;
  417. prox->movetype = MOVETYPE_BOUNCE;
  418. prox->solid = SOLID_BBOX;
  419. prox->s.effects |= EF_GRENADE;
  420. prox->clipmask = MASK_SHOT|CONTENTS_LAVA|CONTENTS_SLIME;
  421. prox->s.renderfx |= RF_IR_VISIBLE;
  422. //FIXME - this needs to be bigger. Has other effects, though. Maybe have to change origin to compensate
  423. // so it sinks in correctly. Also in lavacheck, might have to up the distance
  424. VectorSet (prox->mins, -6, -6, -6);
  425. VectorSet (prox->maxs, 6, 6, 6);
  426. prox->s.modelindex = gi.modelindex ("models/weapons/g_prox/tris.md2");
  427. prox->owner = self;
  428. prox->teammaster = self;
  429. prox->touch = prox_land;
  430. // prox->nextthink = level.time + PROX_TIME_TO_LIVE;
  431. prox->think = Prox_Explode;
  432. prox->dmg = PROX_DAMAGE*damage_multiplier;
  433. prox->classname = "prox";
  434. prox->svflags |= SVF_DAMAGEABLE;
  435. prox->flags |= FL_MECHANICAL;
  436. switch (damage_multiplier)
  437. {
  438. case 1:
  439. prox->nextthink = level.time + PROX_TIME_TO_LIVE;
  440. break;
  441. case 2:
  442. prox->nextthink = level.time + 30;
  443. break;
  444. case 4:
  445. prox->nextthink = level.time + 15;
  446. break;
  447. case 8:
  448. prox->nextthink = level.time + 10;
  449. break;
  450. default:
  451. // if ((g_showlogic) && (g_showlogic->value))
  452. // gi.dprintf ("prox with unknown multiplier %d!\n", damage_multiplier);
  453. prox->nextthink = level.time + PROX_TIME_TO_LIVE;
  454. break;
  455. }
  456. gi.linkentity (prox);
  457. }
  458. #endif
  459. // *************************
  460. // FLAMETHROWER
  461. // *************************
  462. #ifdef INCLUDE_FLAMETHROWER
  463. #define FLAMETHROWER_RADIUS 8
  464. void fire_remove (edict_t *ent)
  465. {
  466. if(ent == ent->owner->teamchain)
  467. ent->owner->teamchain = NULL;
  468. G_FreeEdict(ent);
  469. }
  470. void fire_flame (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed)
  471. {
  472. edict_t *flame;
  473. vec3_t dir;
  474. vec3_t forward, right, up;
  475. vectoangles2 (aimdir, dir);
  476. AngleVectors (dir, forward, right, up);
  477. flame = G_Spawn();
  478. // the origin is the first control point, put it speed forward.
  479. VectorMA(start, speed, forward, flame->s.origin);
  480. // record that velocity
  481. VectorScale (aimdir, speed, flame->velocity);
  482. VectorCopy (dir, flame->s.angles);
  483. flame->movetype = MOVETYPE_NONE;
  484. flame->solid = SOLID_NOT;
  485. VectorSet(flame->mins, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS);
  486. VectorSet(flame->maxs, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS);
  487. flame->s.sound = gi.soundindex ("weapons/flame.wav");
  488. flame->owner = self;
  489. flame->dmg = damage;
  490. flame->classname = "flame";
  491. // clear control points and velocities
  492. VectorCopy (flame->s.origin, flame->flameinfo.pos1);
  493. VectorCopy (flame->velocity, flame->flameinfo.vel1);
  494. VectorCopy (flame->s.origin, flame->flameinfo.pos2);
  495. VectorCopy (flame->velocity, flame->flameinfo.vel2);
  496. VectorCopy (flame->s.origin, flame->flameinfo.pos3);
  497. VectorCopy (flame->velocity, flame->flameinfo.vel3);
  498. VectorCopy (flame->s.origin, flame->flameinfo.pos4);
  499. // hook flame stream to owner
  500. self->teamchain = flame;
  501. gi.linkentity (flame);
  502. }
  503. // fixme - change to use start location, not entity origin
  504. void fire_maintain (edict_t *ent, edict_t *flame, vec3_t start, vec3_t aimdir, int damage, int speed)
  505. {
  506. trace_t tr;
  507. // move the control points out the appropriate direction and velocity
  508. VectorAdd(flame->flameinfo.pos3, flame->flameinfo.vel3, flame->flameinfo.pos4);
  509. VectorAdd(flame->flameinfo.pos2, flame->flameinfo.vel2, flame->flameinfo.pos3);
  510. VectorAdd(flame->flameinfo.pos1, flame->flameinfo.vel1, flame->flameinfo.pos2);
  511. VectorAdd(flame->s.origin, flame->velocity, flame->flameinfo.pos1);
  512. // move the velocities for the control points
  513. VectorCopy(flame->flameinfo.vel2, flame->flameinfo.vel3);
  514. VectorCopy(flame->flameinfo.vel1, flame->flameinfo.vel2);
  515. VectorCopy(flame->velocity, flame->flameinfo.vel1);
  516. // set velocity and location for new control point 0.
  517. VectorMA(start, speed, aimdir, flame->s.origin);
  518. VectorScale(aimdir, speed, flame->velocity);
  519. //
  520. // does it hit a wall? if so, when?
  521. //
  522. // player fire point to flame origin.
  523. tr = gi.trace(start, flame->mins, flame->maxs,
  524. flame->s.origin, flame, MASK_SHOT);
  525. if(tr.fraction == 1.0)
  526. {
  527. // origin to point 1
  528. tr = gi.trace(flame->s.origin, flame->mins, flame->maxs,
  529. flame->flameinfo.pos1, flame, MASK_SHOT);
  530. if(tr.fraction == 1.0)
  531. {
  532. // point 1 to point 2
  533. tr = gi.trace(flame->flameinfo.pos1, flame->mins, flame->maxs,
  534. flame->flameinfo.pos2, flame, MASK_SHOT);
  535. if(tr.fraction == 1.0)
  536. {
  537. // point 2 to point 3
  538. tr = gi.trace(flame->flameinfo.pos2, flame->mins, flame->maxs,
  539. flame->flameinfo.pos3, flame, MASK_SHOT);
  540. if(tr.fraction == 1.0)
  541. {
  542. // point 3 to point 4, point 3 valid
  543. tr = gi.trace(flame->flameinfo.pos3, flame->mins, flame->maxs,
  544. flame->flameinfo.pos4, flame, MASK_SHOT);
  545. if(tr.fraction < 1.0) // point 4 blocked
  546. {
  547. VectorCopy(tr.endpos, flame->flameinfo.pos4);
  548. }
  549. }
  550. else // point 3 blocked, point 2 valid
  551. {
  552. VectorCopy(flame->flameinfo.vel2, flame->flameinfo.vel3);
  553. VectorCopy(tr.endpos, flame->flameinfo.pos3);
  554. VectorCopy(tr.endpos, flame->flameinfo.pos4);
  555. }
  556. }
  557. else // point 2 blocked, point 1 valid
  558. {
  559. VectorCopy(flame->flameinfo.vel1, flame->flameinfo.vel2);
  560. VectorCopy(flame->flameinfo.vel1, flame->flameinfo.vel3);
  561. VectorCopy(tr.endpos, flame->flameinfo.pos2);
  562. VectorCopy(tr.endpos, flame->flameinfo.pos3);
  563. VectorCopy(tr.endpos, flame->flameinfo.pos4);
  564. }
  565. }
  566. else // point 1 blocked, origin valid
  567. {
  568. VectorCopy(flame->velocity, flame->flameinfo.vel1);
  569. VectorCopy(flame->velocity, flame->flameinfo.vel2);
  570. VectorCopy(flame->velocity, flame->flameinfo.vel3);
  571. VectorCopy(tr.endpos, flame->flameinfo.pos1);
  572. VectorCopy(tr.endpos, flame->flameinfo.pos2);
  573. VectorCopy(tr.endpos, flame->flameinfo.pos3);
  574. VectorCopy(tr.endpos, flame->flameinfo.pos4);
  575. }
  576. }
  577. else // origin blocked!
  578. {
  579. // gi.dprintf("point 2 blocked\n");
  580. VectorCopy(flame->velocity, flame->flameinfo.vel1);
  581. VectorCopy(flame->velocity, flame->flameinfo.vel2);
  582. VectorCopy(flame->velocity, flame->flameinfo.vel3);
  583. VectorCopy(tr.endpos, flame->s.origin);
  584. VectorCopy(tr.endpos, flame->flameinfo.pos1);
  585. VectorCopy(tr.endpos, flame->flameinfo.pos2);
  586. VectorCopy(tr.endpos, flame->flameinfo.pos3);
  587. VectorCopy(tr.endpos, flame->flameinfo.pos4);
  588. }
  589. if(tr.fraction < 1.0 && tr.ent->takedamage)
  590. {
  591. T_Damage (tr.ent, flame, ent, flame->velocity, tr.endpos, tr.plane.normal,
  592. damage, 0, DAMAGE_NO_KNOCKBACK | DAMAGE_ENERGY | DAMAGE_FIRE);
  593. }
  594. gi.linkentity(flame);
  595. gi.WriteByte (svc_temp_entity);
  596. gi.WriteByte (TE_FLAME);
  597. gi.WriteShort(ent - g_edicts);
  598. gi.WriteShort(6);
  599. gi.WritePosition (start);
  600. gi.WritePosition (flame->s.origin);
  601. gi.WritePosition (flame->flameinfo.pos1);
  602. gi.WritePosition (flame->flameinfo.pos2);
  603. gi.WritePosition (flame->flameinfo.pos3);
  604. gi.WritePosition (flame->flameinfo.pos4);
  605. gi.multicast (flame->s.origin, MULTICAST_PVS);
  606. }
  607. /*QUAKED trap_flameshooter (1 0 0) (-8 -8 -8) (8 8 8)
  608. */
  609. #define FLAMESHOOTER_VELOCITY 50
  610. #define FLAMESHOOTER_DAMAGE 20
  611. #define FLAMESHOOTER_BURST_VELOCITY 300
  612. #define FLAMESHOOTER_BURST_DAMAGE 30
  613. //#define FLAMESHOOTER_PUFF 1
  614. #define FLAMESHOOTER_STREAM 1
  615. void flameshooter_think (edict_t *self)
  616. {
  617. vec3_t forward, right, up;
  618. edict_t *flame;
  619. if(self->delay)
  620. {
  621. if(self->teamchain)
  622. fire_remove (self->teamchain);
  623. return;
  624. }
  625. self->s.angles[1] += self->speed;
  626. if(self->s.angles[1] > 135 || self->s.angles[1] < 45)
  627. self->speed = -self->speed;
  628. AngleVectors (self->s.angles, forward, right, up);
  629. #ifdef FLAMESHOOTER_STREAM
  630. flame = self->teamchain;
  631. if(!self->teamchain)
  632. fire_flame (self, self->s.origin, forward, FLAMESHOOTER_DAMAGE, FLAMESHOOTER_VELOCITY);
  633. else
  634. fire_maintain (self, flame, self->s.origin, forward, FLAMESHOOTER_DAMAGE, FLAMESHOOTER_VELOCITY);
  635. self->think = flameshooter_think;
  636. self->nextthink = level.time + 0.05;
  637. #else
  638. fire_burst (self, self->s.origin, forward, FLAMESHOOTER_BURST_DAMAGE, FLAMESHOOTER_BURST_VELOCITY);
  639. self->think = flameshooter_think;
  640. self->nextthink = level.time + 0.1;
  641. #endif
  642. }
  643. void flameshooter_use (edict_t *self, edict_t *other, edict_t *activator)
  644. {
  645. if(self->delay)
  646. {
  647. self->delay = 0;
  648. self->think = flameshooter_think;
  649. self->nextthink = level.time + 0.1;
  650. }
  651. else
  652. self->delay = 1;
  653. }
  654. void SP_trap_flameshooter(edict_t *self)
  655. {
  656. vec3_t tempAngles;
  657. self->solid = SOLID_NOT;
  658. self->movetype = MOVETYPE_NONE;
  659. self->delay = 0;
  660. self->use = flameshooter_use;
  661. if(self->delay == 0)
  662. {
  663. self->think = flameshooter_think;
  664. self->nextthink = level.time + 0.1;
  665. }
  666. // self->flags |= FL_NOCLIENT;
  667. self->speed = 10;
  668. // self->speed = 0; // FIXME this stops the spraying
  669. VectorCopy(self->s.angles, tempAngles);
  670. if (!VectorCompare(self->s.angles, vec3_origin))
  671. G_SetMovedir (self->s.angles, self->movedir);
  672. VectorCopy(tempAngles, self->s.angles);
  673. // gi.setmodel (self, self->model);
  674. gi.linkentity (self);
  675. }
  676. // *************************
  677. // fire_burst
  678. // *************************
  679. #define FLAME_BURST_MAX_SIZE 64
  680. #define FLAME_BURST_FRAMES 20
  681. #define FLAME_BURST_MIDPOINT 10
  682. void fire_burst_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  683. {
  684. int powerunits;
  685. int damage, radius;
  686. vec3_t origin;
  687. if (surf && (surf->flags & SURF_SKY))
  688. {
  689. // gi.dprintf("Hit sky. Removed\n");
  690. G_FreeEdict (ent);
  691. return;
  692. }
  693. if(other == ent->owner || ent == other)
  694. return;
  695. // don't let flame puffs blow each other up
  696. if(other->classname && !strcmp(other->classname, ent->classname))
  697. return;
  698. if(ent->waterlevel)
  699. {
  700. // gi.dprintf("Hit water. Removed\n");
  701. G_FreeEdict(ent);
  702. }
  703. if(!(other->svflags & SVF_MONSTER) && !other->client)
  704. {
  705. powerunits = FLAME_BURST_FRAMES - ent->s.frame;
  706. damage = powerunits * 6;
  707. radius = powerunits * 4;
  708. // T_RadiusDamage (inflictor, attacker, damage, ignore, radius)
  709. T_RadiusDamage(ent, ent->owner, damage, ent, radius, DAMAGE_FIRE);
  710. // gi.dprintf("Hit world: %d pts, %d rad\n", damage, radius);
  711. // calculate position for the explosion entity
  712. VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
  713. gi.WriteByte (svc_temp_entity);
  714. gi.WriteByte (TE_PLAIN_EXPLOSION);
  715. gi.WritePosition (origin);
  716. gi.multicast (ent->s.origin, MULTICAST_PVS);
  717. G_FreeEdict (ent);
  718. }
  719. }
  720. void fire_burst_think (edict_t *self)
  721. {
  722. int current_radius;
  723. if(self->waterlevel)
  724. {
  725. G_FreeEdict(self);
  726. return;
  727. }
  728. self->s.frame++;
  729. if(self->s.frame >= FLAME_BURST_FRAMES)
  730. {
  731. G_FreeEdict(self);
  732. return;
  733. }
  734. else if(self->s.frame < FLAME_BURST_MIDPOINT)
  735. {
  736. current_radius = (FLAME_BURST_MAX_SIZE / FLAME_BURST_MIDPOINT) * self->s.frame;
  737. }
  738. else
  739. {
  740. current_radius = (FLAME_BURST_MAX_SIZE / FLAME_BURST_MIDPOINT) * (FLAME_BURST_FRAMES - self->s.frame);
  741. }
  742. if(self->s.frame == 3)
  743. self->s.skinnum = 1;
  744. else if (self->s.frame == 7)
  745. self->s.skinnum = 2;
  746. else if (self->s.frame == 10)
  747. self->s.skinnum = 3;
  748. else if (self->s.frame == 13)
  749. self->s.skinnum = 4;
  750. else if (self->s.frame == 16)
  751. self->s.skinnum = 5;
  752. else if (self->s.frame == 19)
  753. self->s.skinnum = 6;
  754. if(current_radius < 8)
  755. current_radius = 8;
  756. else if(current_radius > FLAME_BURST_MAX_SIZE)
  757. current_radius = FLAME_BURST_MAX_SIZE;
  758. T_RadiusDamage(self, self->owner, self->dmg, self, current_radius, DAMAGE_FIRE);
  759. self->think = fire_burst_think;
  760. self->nextthink = level.time + 0.1;
  761. }
  762. void fire_burst (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed)
  763. {
  764. edict_t *flame;
  765. vec3_t dir;
  766. vec3_t baseVel;
  767. vec3_t forward, right, up;
  768. vectoangles2 (aimdir, dir);
  769. AngleVectors (dir, forward, right, up);
  770. flame = G_Spawn();
  771. VectorCopy(start, flame->s.origin);
  772. // VectorScale (aimdir, speed, flame->velocity);
  773. // scale down so only 30% of player's velocity is taken into account.
  774. VectorScale (self->velocity, 0.3, baseVel);
  775. VectorMA(baseVel, speed, aimdir, flame->velocity);
  776. VectorCopy (dir, flame->s.angles);
  777. flame->movetype = MOVETYPE_FLY;
  778. flame->solid = SOLID_TRIGGER;
  779. VectorSet(flame->mins, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS);
  780. VectorSet(flame->maxs, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS);
  781. flame->s.sound = gi.soundindex ("weapons/flame.wav");
  782. flame->s.modelindex = gi.modelindex ("models/projectiles/puff/tris.md2");
  783. flame->owner = self;
  784. flame->touch = fire_burst_touch;
  785. flame->think = fire_burst_think;
  786. flame->nextthink = level.time + 0.1;
  787. flame->dmg = damage;
  788. flame->classname = "flameburst";
  789. flame->s.effects = EF_FIRE_PUFF;
  790. gi.linkentity (flame);
  791. }
  792. #endif
  793. // *************************
  794. // INCENDIARY GRENADES
  795. // *************************
  796. #ifdef INCLUDE_INCENDIARY
  797. void FireThink (edict_t *ent)
  798. {
  799. if(level.time > ent->wait)
  800. G_FreeEdict(ent);
  801. else
  802. {
  803. ent->s.frame++;
  804. if(ent->s.frame>10)
  805. ent->s.frame = 0;
  806. ent->nextthink = level.time + 0.05;
  807. ent->think = FireThink;
  808. }
  809. }
  810. #define FIRE_HEIGHT 64
  811. #define FIRE_RADIUS 64
  812. #define FIRE_DAMAGE 3
  813. #define FIRE_DURATION 15
  814. edict_t *StartFire(edict_t *fireOwner, vec3_t fireOrigin, float fireDuration, float fireDamage)
  815. {
  816. edict_t *fire;
  817. fire = G_Spawn();
  818. VectorCopy (fireOrigin, fire->s.origin);
  819. fire->movetype = MOVETYPE_TOSS;
  820. fire->solid = SOLID_TRIGGER;
  821. VectorSet(fire->mins, -FIRE_RADIUS, -FIRE_RADIUS, 0);
  822. VectorSet(fire->maxs, FIRE_RADIUS, FIRE_RADIUS, FIRE_HEIGHT);
  823. fire->s.sound = gi.soundindex ("weapons/incend.wav");
  824. fire->s.modelindex = gi.modelindex ("models/objects/fire/tris.md2");
  825. fire->owner = fireOwner;
  826. fire->touch = hurt_touch;
  827. fire->nextthink = level.time + 0.05;
  828. fire->wait = level.time + fireDuration;
  829. fire->think = FireThink;
  830. // fire->nextthink = level.time + fireDuration;
  831. // fire->think = G_FreeEdict;
  832. fire->dmg = fireDamage;
  833. fire->classname = "incendiary_fire";
  834. gi.linkentity (fire);
  835. // gi.sound (fire, CHAN_VOICE, gi.soundindex ("weapons/incend.wav"), 1, ATTN_NORM, 0);
  836. return fire;
  837. }
  838. static void Incendiary_Explode (edict_t *ent)
  839. {
  840. vec3_t origin;
  841. if (ent->owner->client)
  842. PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
  843. //FIXME: if we are onground then raise our Z just a bit since we are a point?
  844. T_RadiusDamage(ent, ent->owner, ent->dmg, NULL, ent->dmg_radius, DAMAGE_FIRE);
  845. VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
  846. gi.WriteByte (svc_temp_entity);
  847. if (ent->groundentity)
  848. gi.WriteByte (TE_GRENADE_EXPLOSION);
  849. else
  850. gi.WriteByte (TE_ROCKET_EXPLOSION);
  851. gi.WritePosition (origin);
  852. gi.multicast (ent->s.origin, MULTICAST_PVS);
  853. StartFire(ent->owner, ent->s.origin, FIRE_DURATION, FIRE_DAMAGE);
  854. G_FreeEdict (ent);
  855. }
  856. static void Incendiary_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  857. {
  858. if (other == ent->owner)
  859. return;
  860. if (surf && (surf->flags & SURF_SKY))
  861. {
  862. G_FreeEdict (ent);
  863. return;
  864. }
  865. if (!(other->svflags & SVF_MONSTER) && !(ent->client))
  866. // if (!other->takedamage)
  867. {
  868. if (ent->spawnflags & 1)
  869. {
  870. if (random() > 0.5)
  871. gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
  872. else
  873. gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
  874. }
  875. else
  876. {
  877. if (random() > 0.5)
  878. gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
  879. else
  880. gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb2b.wav"), 1, ATTN_NORM, 0);
  881. }
  882. return;
  883. }
  884. Incendiary_Explode (ent);
  885. }
  886. void fire_incendiary_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
  887. {
  888. edict_t *grenade;
  889. vec3_t dir;
  890. vec3_t forward, right, up;
  891. vectoangles2 (aimdir, dir);
  892. AngleVectors (dir, forward, right, up);
  893. grenade = G_Spawn();
  894. VectorCopy (start, grenade->s.origin);
  895. VectorScale (aimdir, speed, grenade->velocity);
  896. VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
  897. VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
  898. VectorSet (grenade->avelocity, 300, 300, 300);
  899. grenade->movetype = MOVETYPE_BOUNCE;
  900. grenade->clipmask = MASK_SHOT;
  901. grenade->solid = SOLID_BBOX;
  902. grenade->s.effects |= EF_GRENADE;
  903. // if (self->client)
  904. // grenade->s.effects &= ~EF_TELEPORT;
  905. VectorClear (grenade->mins);
  906. VectorClear (grenade->maxs);
  907. grenade->s.modelindex = gi.modelindex ("models/projectiles/incend/tris.md2");
  908. grenade->owner = self;
  909. grenade->touch = Incendiary_Touch;
  910. grenade->nextthink = level.time + timer;
  911. grenade->think = Incendiary_Explode;
  912. grenade->dmg = damage;
  913. grenade->dmg_radius = damage_radius;
  914. grenade->classname = "incendiary_grenade";
  915. gi.linkentity (grenade);
  916. }
  917. #endif
  918. // *************************
  919. // MELEE WEAPONS
  920. // *************************
  921. #ifdef INCLUDE_MELEE
  922. void fire_player_melee (edict_t *self, vec3_t start, vec3_t aim, int reach, int damage, int kick, int quiet, int mod)
  923. {
  924. vec3_t forward, right, up;
  925. vec3_t v;
  926. vec3_t point;
  927. trace_t tr;
  928. vectoangles2 (aim, v);
  929. AngleVectors (v, forward, right, up);
  930. VectorNormalize (forward);
  931. VectorMA( start, reach, forward, point);
  932. //see if the hit connects
  933. tr = gi.trace(start, NULL, NULL, point, self, MASK_SHOT);
  934. if(tr.fraction == 1.0)
  935. {
  936. if(!quiet)
  937. gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/swish.wav"), 1, ATTN_NORM, 0);
  938. //FIXME some sound here?
  939. return;
  940. }
  941. if(tr.ent->takedamage == DAMAGE_YES || tr.ent->takedamage == DAMAGE_AIM)
  942. {
  943. // pull the player forward if you do damage
  944. VectorMA(self->velocity, 75, forward, self->velocity);
  945. VectorMA(self->velocity, 75, up, self->velocity);
  946. // do the damage
  947. // FIXME - make the damage appear at right spot and direction
  948. if(mod == MOD_CHAINFIST)
  949. T_Damage (tr.ent, self, self, vec3_origin, tr.ent->s.origin, vec3_origin, damage, kick/2,
  950. DAMAGE_DESTROY_ARMOR | DAMAGE_NO_KNOCKBACK, mod);
  951. else
  952. T_Damage (tr.ent, self, self, vec3_origin, tr.ent->s.origin, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, mod);
  953. if(!quiet)
  954. gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/meatht.wav"), 1, ATTN_NORM, 0);
  955. }
  956. else
  957. {
  958. if(!quiet)
  959. gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/tink1.wav"), 1, ATTN_NORM, 0);
  960. VectorScale (tr.plane.normal, 256, point);
  961. gi.WriteByte (svc_temp_entity);
  962. gi.WriteByte (TE_GUNSHOT);
  963. gi.WritePosition (tr.endpos);
  964. gi.WriteDir (point);
  965. gi.multicast (tr.endpos, MULTICAST_PVS);
  966. }
  967. }
  968. #endif
  969. // *************************
  970. // NUKE
  971. // *************************
  972. #ifdef INCLUDE_NUKE
  973. #define NUKE_DELAY 4
  974. #define NUKE_TIME_TO_LIVE 6
  975. //#define NUKE_TIME_TO_LIVE 40
  976. #define NUKE_RADIUS 512
  977. #define NUKE_DAMAGE 400
  978. #define NUKE_QUAKE_TIME 3
  979. #define NUKE_QUAKE_STRENGTH 100
  980. void Nuke_Quake (edict_t *self)
  981. {
  982. int i;
  983. edict_t *e;
  984. if (self->last_move_time < level.time)
  985. {
  986. gi.positioned_sound (self->s.origin, self, CHAN_AUTO, self->noise_index, 0.75, ATTN_NONE, 0);
  987. self->last_move_time = level.time + 0.5;
  988. }
  989. for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++)
  990. {
  991. if (!e->inuse)
  992. continue;
  993. if (!e->client)
  994. continue;
  995. if (!e->groundentity)
  996. continue;
  997. e->groundentity = NULL;
  998. e->velocity[0] += crandom()* 150;
  999. e->velocity[1] += crandom()* 150;
  1000. e->velocity[2] = self->speed * (100.0 / e->mass);
  1001. }
  1002. if (level.time < self->timestamp)
  1003. self->nextthink = level.time + FRAMETIME;
  1004. else
  1005. G_FreeEdict (self);
  1006. }
  1007. static void Nuke_Explode (edict_t *ent)
  1008. {
  1009. // vec3_t origin;
  1010. // nuke_framenum = level.framenum + 20;
  1011. if (ent->teammaster->client)
  1012. PlayerNoise(ent->teammaster, ent->s.origin, PNOISE_IMPACT);
  1013. T_RadiusNukeDamage(ent, ent->teammaster, ent->dmg, ent, ent->dmg_radius, MOD_NUKE);
  1014. // VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
  1015. if (ent->dmg > NUKE_DAMAGE)
  1016. gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
  1017. gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/grenlx1a.wav"), 1, ATTN_NONE, 0);
  1018. /*
  1019. gi.WriteByte (svc_temp_entity);
  1020. if (ent->groundentity)
  1021. gi.WriteByte (TE_GRENADE_EXPLOSION);
  1022. else
  1023. gi.WriteByte (TE_ROCKET_EXPLOSION);
  1024. gi.WritePosition (origin);
  1025. gi.multicast (ent->s.origin, MULTICAST_PVS);
  1026. */
  1027. // BecomeExplosion1(ent);
  1028. gi.WriteByte (svc_temp_entity);
  1029. gi.WriteByte (TE_EXPLOSION1_BIG);
  1030. gi.WritePosition (ent->s.origin);
  1031. gi.multicast (ent->s.origin, MULTICAST_PVS);
  1032. gi.WriteByte (svc_temp_entity);
  1033. gi.WriteByte (TE_NUKEBLAST);
  1034. gi.WritePosition (ent->s.origin);
  1035. gi.multicast (ent->s.origin, MULTICAST_ALL);
  1036. // become a quake
  1037. ent->svflags |= SVF_NOCLIENT;
  1038. ent->noise_index = gi.soundindex ("world/rumble.wav");
  1039. ent->think = Nuke_Quake;
  1040. ent->speed = NUKE_QUAKE_STRENGTH;
  1041. ent->timestamp = level.time + NUKE_QUAKE_TIME;
  1042. ent->nextthink = level.time + FRAMETIME;
  1043. ent->last_move_time = 0;
  1044. }
  1045. void nuke_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  1046. {
  1047. self->takedamage = DAMAGE_NO;
  1048. if ((attacker) && !(strcmp(attacker->classname, "nuke")))
  1049. {
  1050. // if ((g_showlogic) && (g_showlogic->value))
  1051. // gi.dprintf ("nuke nuked by a nuke, not nuking\n");
  1052. G_FreeEdict (self);
  1053. return;
  1054. }
  1055. Nuke_Explode(self);
  1056. }
  1057. void Nuke_Think(edict_t *ent)
  1058. {
  1059. float attenuation, default_atten = 1.8;
  1060. int damage_multiplier, muzzleflash;
  1061. // gi.dprintf ("player range: %2.2f damage radius: %2.2f\n", realrange (ent, ent->teammaster), ent->dmg_radius*2);
  1062. damage_multiplier = ent->dmg/NUKE_DAMAGE;
  1063. switch (damage_multiplier)
  1064. {
  1065. case 1:
  1066. attenuation = default_atten/1.4;
  1067. muzzleflash = MZ_NUKE1;
  1068. break;
  1069. case 2:
  1070. attenuation = default_atten/2.0;
  1071. muzzleflash = MZ_NUKE2;
  1072. break;
  1073. case 4:
  1074. attenuation = default_atten/3.0;
  1075. muzzleflash = MZ_NUKE4;
  1076. break;
  1077. case 8:
  1078. attenuation = default_atten/5.0;
  1079. muzzleflash = MZ_NUKE8;
  1080. break;
  1081. default:
  1082. // if ((g_showlogic) && (g_showlogic->value))
  1083. // gi.dprintf ("default attenuation used for nuke!\n");
  1084. attenuation = default_atten;
  1085. muzzleflash = MZ_NUKE1;
  1086. break;
  1087. }
  1088. if(ent->wait < level.time)
  1089. Nuke_Explode(ent);
  1090. else if (level.time >= (ent->wait - NUKE_TIME_TO_LIVE))
  1091. {
  1092. ent->s.frame++;
  1093. // if ((g_showlogic) && (g_showlogic->value))
  1094. // gi.dprintf ("nuke frame %d\n", ent->s.frame);
  1095. if(ent->s.frame > 11)
  1096. ent->s.frame = 6;
  1097. if (gi.pointcontents (ent->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA))
  1098. {
  1099. Nuke_Explode (ent);
  1100. return;
  1101. }
  1102. ent->think = Nuke_Think;
  1103. ent->nextthink = level.time + 0.1;
  1104. ent->health = 1;
  1105. ent->owner = NULL;
  1106. gi.WriteByte (svc_muzzleflash);
  1107. gi.WriteShort (ent-g_edicts);
  1108. gi.WriteByte (muzzleflash);
  1109. gi.multicast (ent->s.origin, MULTICAST_PVS);
  1110. if (ent->timestamp <= level.time)
  1111. {
  1112. /* gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn.wav"), 1, ATTN_NORM, 0);
  1113. ent->timestamp += 10.0;
  1114. }
  1115. */
  1116. if ((ent->wait - level.time) <= (NUKE_TIME_TO_LIVE/2.0))
  1117. {
  1118. // ent->s.sound = gi.soundindex ("weapons/nukewarn.wav");
  1119. // gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
  1120. gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0);
  1121. // gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
  1122. // gi.dprintf ("time %2.2f\n", ent->wait-level.time);
  1123. ent->timestamp = level.time + 0.3;
  1124. }
  1125. else
  1126. {
  1127. gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0);
  1128. // gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
  1129. ent->timestamp = level.time + 0.5;
  1130. // gi.dprintf ("time %2.2f\n", ent->wait-level.time);
  1131. }
  1132. }
  1133. }
  1134. else
  1135. {
  1136. if (ent->timestamp <= level.time)
  1137. {
  1138. gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0);
  1139. // gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
  1140. // gi.dprintf ("time %2.2f\n", ent->wait-level.time);
  1141. ent->timestamp = level.time + 1.0;
  1142. }
  1143. ent->nextthink = level.time + FRAMETIME;
  1144. }
  1145. }
  1146. void nuke_bounce (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  1147. {
  1148. if (random() > 0.5)
  1149. gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
  1150. else
  1151. gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
  1152. }
  1153. extern byte P_DamageModifier(edict_t *ent);
  1154. void fire_nuke (edict_t *self, vec3_t start, vec3_t aimdir, int speed)
  1155. {
  1156. edict_t *nuke;
  1157. vec3_t dir;
  1158. vec3_t forward, right, up;
  1159. int damage_modifier;
  1160. damage_modifier = (int) P_DamageModifier (self);
  1161. vectoangles2 (aimdir, dir);
  1162. AngleVectors (dir, forward, right, up);
  1163. nuke = G_Spawn();
  1164. VectorCopy (start, nuke->s.origin);
  1165. VectorScale (aimdir, speed, nuke->velocity);
  1166. VectorMA (nuke->velocity, 200 + crandom() * 10.0, up, nuke->velocity);
  1167. VectorMA (nuke->velocity, crandom() * 10.0, right, nuke->velocity);
  1168. VectorClear (nuke->avelocity);
  1169. VectorClear (nuke->s.angles);
  1170. nuke->movetype = MOVETYPE_BOUNCE;
  1171. nuke->clipmask = MASK_SHOT;
  1172. nuke->solid = SOLID_BBOX;
  1173. nuke->s.effects |= EF_GRENADE;
  1174. nuke->s.renderfx |= RF_IR_VISIBLE;
  1175. VectorSet (nuke->mins, -8, -8, 0);
  1176. VectorSet (nuke->maxs, 8, 8, 16);
  1177. nuke->s.modelindex = gi.modelindex ("models/weapons/g_nuke/tris.md2");
  1178. nuke->owner = self;
  1179. nuke->teammaster = self;
  1180. nuke->nextthink = level.time + FRAMETIME;
  1181. nuke->wait = level.time + NUKE_DELAY + NUKE_TIME_TO_LIVE;
  1182. nuke->think = Nuke_Think;
  1183. nuke->touch = nuke_bounce;
  1184. nuke->health = 10000;
  1185. nuke->takedamage = DAMAGE_YES;
  1186. nuke->svflags |= SVF_DAMAGEABLE;
  1187. nuke->dmg = NUKE_DAMAGE * damage_modifier;
  1188. if (damage_modifier == 1)
  1189. nuke->dmg_radius = NUKE_RADIUS;
  1190. else
  1191. nuke->dmg_radius = NUKE_RADIUS + NUKE_RADIUS*(0.25*(float)damage_modifier);
  1192. // this yields 1.0, 1.5, 2.0, 3.0 times radius
  1193. // if ((g_showlogic) && (g_showlogic->value))
  1194. // gi.dprintf ("nuke modifier = %d, damage = %d, radius = %f\n", damage_modifier, nuke->dmg, nuke->dmg_radius);
  1195. nuke->classname = "nuke";
  1196. nuke->die = nuke_die;
  1197. gi.linkentity (nuke);
  1198. }
  1199. #endif
  1200. // *************************
  1201. // TESLA
  1202. // *************************
  1203. #ifdef INCLUDE_TESLA
  1204. #define TESLA_TIME_TO_LIVE 30
  1205. #define TESLA_DAMAGE_RADIUS 128
  1206. #define TESLA_DAMAGE 3 // 3
  1207. #define TESLA_KNOCKBACK 8
  1208. #define TESLA_ACTIVATE_TIME 3
  1209. #define TESLA_EXPLOSION_DAMAGE_MULT 50 // this is the amount the damage is multiplied by for underwater explosions
  1210. #define TESLA_EXPLOSION_RADIUS 200
  1211. void tesla_remove (edict_t *self)
  1212. {
  1213. edict_t *cur, *next;
  1214. self->takedamage = DAMAGE_NO;
  1215. if(self->teamchain)
  1216. {
  1217. cur = self->teamchain;
  1218. while(cur)
  1219. {
  1220. next = cur->teamchain;
  1221. G_FreeEdict ( cur );
  1222. cur = next;
  1223. }
  1224. }
  1225. else if (self->air_finished)
  1226. gi.dprintf ("tesla without a field!\n");
  1227. self->owner = self->teammaster; // Going away, set the owner correctly.
  1228. // PGM - grenade explode does damage to self->enemy
  1229. self->enemy = NULL;
  1230. // play quad sound if quadded and an underwater explosion
  1231. if ((self->dmg_radius) && (self->dmg > (TESLA_DAMAGE*TESLA_EXPLOSION_DAMAGE_MULT)))
  1232. gi.sound(self, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
  1233. Grenade_Explode(self);
  1234. }
  1235. void tesla_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  1236. {
  1237. // gi.dprintf("tesla killed\n");
  1238. tesla_remove(self);
  1239. }
  1240. void tesla_blow (edict_t *self)
  1241. {
  1242. // T_RadiusDamage(self, self->owner, TESLA_EXPLOSION_DAMAGE, NULL, TESLA_EXPLOSION_RADIUS, MOD_TESLA);
  1243. self->dmg = self->dmg * TESLA_EXPLOSION_DAMAGE_MULT;
  1244. self->dmg_radius = TESLA_EXPLOSION_RADIUS;
  1245. tesla_remove(self);
  1246. }
  1247. void tesla_zap (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  1248. {
  1249. }
  1250. void tesla_think_active (edict_t *self)
  1251. {
  1252. int i,num;
  1253. edict_t *touch[MAX_EDICTS], *hit;
  1254. vec3_t dir, start;
  1255. trace_t tr;
  1256. if(level.time > self->air_finished)
  1257. {
  1258. tesla_remove(self);
  1259. return;
  1260. }
  1261. VectorCopy(self->s.origin, start);
  1262. start[2] += 16;
  1263. num = gi.BoxEdicts(self->teamchain->absmin, self->teamchain->absmax, touch, MAX_EDICTS, AREA_SOLID);
  1264. for(i=0;i<num;i++)
  1265. {
  1266. // if the tesla died while zapping things, stop zapping.
  1267. if(!(self->inuse))
  1268. break;
  1269. hit=touch[i];
  1270. if(!hit->inuse)
  1271. continue;
  1272. if(hit == self)
  1273. continue;
  1274. if(hit->health < 1)
  1275. continue;
  1276. // don't hit clients in single-player or coop
  1277. if(hit->client)
  1278. if (coop->value || !deathmatch->value)
  1279. continue;
  1280. if(!(hit->svflags & (SVF_MONSTER | SVF_DAMAGEABLE)) && !hit->client)
  1281. continue;
  1282. tr = gi.trace(start, vec3_origin, vec3_origin, hit->s.origin, self, MASK_SHOT);
  1283. if(tr.fraction==1 || tr.ent==hit)// || tr.ent->client || (tr.ent->svflags & (SVF_MONSTER | SVF_DAMAGEABLE)))
  1284. {
  1285. VectorSubtract(hit->s.origin, start, dir);
  1286. // PMM - play quad sound if it's above the "normal" damage
  1287. if (self->dmg > TESLA_DAMAGE)
  1288. gi.sound(self, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
  1289. // PGM - don't do knockback to walking monsters
  1290. if((hit->svflags & SVF_MONSTER) && !(hit->flags & (FL_FLY|FL_SWIM)))
  1291. T_Damage (hit, self, self->teammaster, dir, tr.endpos, tr.plane.normal,
  1292. self->dmg, 0, 0, MOD_TESLA);
  1293. else
  1294. T_Damage (hit, self, self->teammaster, dir, tr.endpos, tr.plane.normal,
  1295. self->dmg, TESLA_KNOCKBACK, 0, MOD_TESLA);
  1296. gi.WriteByte (svc_temp_entity);
  1297. gi.WriteByte (TE_LIGHTNING);
  1298. gi.WriteShort (hit - g_edicts); // destination entity
  1299. gi.WriteShort (self - g_edicts); // source entity
  1300. gi.WritePosition (tr.endpos);
  1301. gi.WritePosition (start);
  1302. gi.multicast (start, MULTICAST_PVS);
  1303. }
  1304. }
  1305. if(self->inuse)
  1306. {
  1307. self->think = tesla_think_active;
  1308. self->nextthink = level.time + FRAMETIME;
  1309. }
  1310. }
  1311. void tesla_activate (edict_t *self)
  1312. {
  1313. edict_t *trigger;
  1314. edict_t *search;
  1315. if (gi.pointcontents (self->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_WATER))
  1316. {
  1317. tesla_blow (self);
  1318. return;
  1319. }
  1320. // only check for spawn points in deathmatch
  1321. if (deathmatch->value)
  1322. {
  1323. search = NULL;
  1324. while ((search = findradius(search, self->s.origin, 1.5*TESLA_DAMAGE_RADIUS)) != NULL)
  1325. {
  1326. //if (!search->takedamage)
  1327. // continue;
  1328. // if it's a monster or player with health > 0
  1329. // or it's a deathmatch start point
  1330. // and we can see it
  1331. // blow up
  1332. if(search->classname)
  1333. {
  1334. if ( ( (!strcmp(search->classname, "info_player_deathmatch"))
  1335. || (!strcmp(search->classname, "info_player_start"))
  1336. || (!strcmp(search->classname, "info_player_coop"))
  1337. || (!strcmp(search->classname, "misc_teleporter_dest"))
  1338. )
  1339. && (visible (search, self))
  1340. )
  1341. {
  1342. // if ((g_showlogic) && (g_showlogic->value))
  1343. // gi.dprintf ("Tesla to close to %s, removing!\n", search->classname);
  1344. tesla_remove (self);
  1345. return;
  1346. }
  1347. }
  1348. }
  1349. }
  1350. trigger = G_Spawn();
  1351. // if (trigger->nextthink)
  1352. // {
  1353. // if ((g_showlogic) && (g_showlogic->value))
  1354. // gi.dprintf ("tesla_activate: fixing nextthink\n");
  1355. // trigger->nextthink = 0;
  1356. // }
  1357. VectorCopy (self->s.origin, trigger->s.origin);
  1358. VectorSet (trigger->mins, -TESLA_DAMAGE_RADIUS, -TESLA_DAMAGE_RADIUS, self->mins[2]);
  1359. VectorSet (trigger->maxs, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS);
  1360. trigger->movetype = MOVETYPE_NONE;
  1361. trigger->solid = SOLID_TRIGGER;
  1362. trigger->owner = self;
  1363. trigger->touch = tesla_zap;
  1364. trigger->classname = "tesla trigger";
  1365. // doesn't need to be marked as a teamslave since the move code for bounce looks for teamchains
  1366. gi.linkentity (trigger);
  1367. VectorClear (self->s.angles);
  1368. // clear the owner if in deathmatch
  1369. if (deathmatch->value)
  1370. self->owner = NULL;
  1371. self->teamchain = trigger;
  1372. self->think = tesla_think_active;
  1373. self->nextthink = level.time + FRAMETIME;
  1374. self->air_finished = level.time + TESLA_TIME_TO_LIVE;
  1375. }
  1376. void tesla_think (edict_t *ent)
  1377. {
  1378. if (gi.pointcontents (ent->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA))
  1379. {
  1380. tesla_remove (ent);
  1381. return;
  1382. }
  1383. VectorClear (ent->s.angles);
  1384. if(!(ent->s.frame))
  1385. gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/teslaopen.wav"), 1, ATTN_NORM, 0);
  1386. ent->s.frame++;
  1387. if(ent->s.frame > 14)
  1388. {
  1389. ent->s.frame = 14;
  1390. ent->think = tesla_activate;
  1391. ent->nextthink = level.time + 0.1;
  1392. }
  1393. else
  1394. {
  1395. if(ent->s.frame > 9)
  1396. {
  1397. if(ent->s.frame == 10)
  1398. {
  1399. if (ent->owner && ent->owner->client)
  1400. {
  1401. PlayerNoise(ent->owner, ent->s.origin, PNOISE_WEAPON); // PGM
  1402. }
  1403. ent->s.skinnum = 1;
  1404. }
  1405. else if(ent->s.frame == 12)
  1406. ent->s.skinnum = 2;
  1407. else if(ent->s.frame == 14)
  1408. ent->s.skinnum = 3;
  1409. }
  1410. ent->think = tesla_think;
  1411. ent->nextthink = level.time + 0.1;
  1412. }
  1413. }
  1414. void tesla_lava (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  1415. {
  1416. vec3_t land_point;
  1417. if (plane->normal)
  1418. {
  1419. VectorMA (ent->s.origin, -20.0, plane->normal, land_point);
  1420. if (gi.pointcontents (land_point) & (CONTENTS_SLIME|CONTENTS_LAVA))
  1421. {
  1422. tesla_blow (ent);
  1423. return;
  1424. }
  1425. }
  1426. if (random() > 0.5)
  1427. gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
  1428. else
  1429. gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
  1430. }
  1431. void fire_tesla (edict_t *self, vec3_t start, vec3_t aimdir, int damage_multiplier, int speed)
  1432. {
  1433. edict_t *tesla;
  1434. vec3_t dir;
  1435. vec3_t forward, right, up;
  1436. vectoangles2 (aimdir, dir);
  1437. AngleVectors (dir, forward, right, up);
  1438. tesla = G_Spawn();
  1439. VectorCopy (start, tesla->s.origin);
  1440. VectorScale (aimdir, speed, tesla->velocity);
  1441. VectorMA (tesla->velocity, 200 + crandom() * 10.0, up, tesla->velocity);
  1442. VectorMA (tesla->velocity, crandom() * 10.0, right, tesla->velocity);
  1443. // VectorCopy (dir, tesla->s.angles);
  1444. VectorClear (tesla->s.angles);
  1445. tesla->movetype = MOVETYPE_BOUNCE;
  1446. tesla->solid = SOLID_BBOX;
  1447. tesla->s.effects |= EF_GRENADE;
  1448. tesla->s.renderfx |= RF_IR_VISIBLE;
  1449. // VectorClear (tesla->mins);
  1450. // VectorClear (tesla->maxs);
  1451. VectorSet (tesla->mins, -12, -12, 0);
  1452. VectorSet (tesla->maxs, 12, 12, 20);
  1453. tesla->s.modelindex = gi.modelindex ("models/weapons/g_tesla/tris.md2");
  1454. tesla->owner = self; // PGM - we don't want it owned by self YET.
  1455. tesla->teammaster = self;
  1456. tesla->wait = level.time + TESLA_TIME_TO_LIVE;
  1457. tesla->think = tesla_think;
  1458. tesla->nextthink = level.time + TESLA_ACTIVATE_TIME;
  1459. // blow up on contact with lava & slime code
  1460. tesla->touch = tesla_lava;
  1461. if(deathmatch->value)
  1462. // PMM - lowered from 50 - 7/29/1998
  1463. tesla->health = 20;
  1464. else
  1465. tesla->health = 30; // FIXME - change depending on skill?
  1466. tesla->takedamage = DAMAGE_YES;
  1467. tesla->die = tesla_die;
  1468. tesla->dmg = TESLA_DAMAGE*damage_multiplier;
  1469. // tesla->dmg = 0;
  1470. tesla->classname = "tesla";
  1471. tesla->svflags |= SVF_DAMAGEABLE;
  1472. tesla->clipmask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA;
  1473. tesla->flags |= FL_MECHANICAL;
  1474. gi.linkentity (tesla);
  1475. }
  1476. #endif
  1477. // *************************
  1478. // HEATBEAM
  1479. // *************************
  1480. #ifdef INCLUDE_BEAMS
  1481. static void fire_beams (edict_t *self, vec3_t start, vec3_t aimdir, vec3_t offset, int damage, int kick, int te_beam, int te_impact, int mod)
  1482. {
  1483. trace_t tr;
  1484. vec3_t dir;
  1485. vec3_t forward, right, up;
  1486. vec3_t end;
  1487. vec3_t water_start, endpoint;
  1488. qboolean water = false, underwater = false;
  1489. int content_mask = MASK_SHOT | MASK_WATER;
  1490. vec3_t beam_endpt;
  1491. // tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT);
  1492. // if (!(tr.fraction < 1.0))
  1493. // {
  1494. vectoangles2 (aimdir, dir);
  1495. AngleVectors (dir, forward, right, up);
  1496. VectorMA (start, 8192, forward, end);
  1497. if (gi.pointcontents (start) & MASK_WATER)
  1498. {
  1499. // gi.dprintf ("Heat beam under water\n");
  1500. underwater = true;
  1501. VectorCopy (start, water_start);
  1502. content_mask &= ~MASK_WATER;
  1503. }
  1504. tr = gi.trace (start, NULL, NULL, end, self, content_mask);
  1505. // see if we hit water
  1506. if (tr.contents & MASK_WATER)
  1507. {
  1508. water = true;
  1509. VectorCopy (tr.endpos, water_start);
  1510. if (!VectorCompare (start, tr.endpos))
  1511. {
  1512. gi.WriteByte (svc_temp_entity);
  1513. gi.WriteByte (TE_HEATBEAM_SPARKS);
  1514. // gi.WriteByte (50);
  1515. gi.WritePosition (water_start);
  1516. gi.WriteDir (tr.plane.normal);
  1517. // gi.WriteByte (8);
  1518. // gi.WriteShort (60);
  1519. gi.multicast (tr.endpos, MULTICAST_PVS);
  1520. }
  1521. // re-trace ignoring water this time
  1522. tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT);
  1523. }
  1524. VectorCopy (tr.endpos, endpoint);
  1525. // }
  1526. // halve the damage if target underwater
  1527. if (water)
  1528. {
  1529. damage = damage /2;
  1530. }
  1531. // send gun puff / flash
  1532. if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
  1533. {
  1534. if (tr.fraction < 1.0)
  1535. {
  1536. if (tr.ent->takedamage)
  1537. {
  1538. T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_ENERGY, mod);
  1539. }
  1540. else
  1541. {
  1542. if ((!water) && (strncmp (tr.surface->name, "sky", 3)))
  1543. {
  1544. // This is the truncated steam entry - uses 1+1+2 extra bytes of data
  1545. gi.WriteByte (svc_temp_entity);
  1546. gi.WriteByte (TE_HEATBEAM_STEAM);
  1547. // gi.WriteByte (20);
  1548. gi.WritePosition (tr.endpos);
  1549. gi.WriteDir (tr.plane.normal);
  1550. // gi.WriteByte (0xe0);
  1551. // gi.WriteShort (60);
  1552. gi.multicast (tr.endpos, MULTICAST_PVS);
  1553. if (self->client)
  1554. PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
  1555. }
  1556. }
  1557. }
  1558. }
  1559. // if went through water, determine where the end and make a bubble trail
  1560. if ((water) || (underwater))
  1561. {
  1562. vec3_t pos;
  1563. VectorSubtract (tr.endpos, water_start, dir);
  1564. VectorNormalize (dir);
  1565. VectorMA (tr.endpos, -2, dir, pos);
  1566. if (gi.pointcontents (pos) & MASK_WATER)
  1567. VectorCopy (pos, tr.endpos);
  1568. else
  1569. tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER);
  1570. VectorAdd (water_start, tr.endpos, pos);
  1571. VectorScale (pos, 0.5, pos);
  1572. gi.WriteByte (svc_temp_entity);
  1573. gi.WriteByte (TE_BUBBLETRAIL2);
  1574. // gi.WriteByte (8);
  1575. gi.WritePosition (water_start);
  1576. gi.WritePosition (tr.endpos);
  1577. gi.multicast (pos, MULTICAST_PVS);
  1578. }
  1579. if ((!underwater) && (!water))
  1580. {
  1581. VectorCopy (tr.endpos, beam_endpt);
  1582. }
  1583. else
  1584. {
  1585. VectorCopy (endpoint, beam_endpt);
  1586. }
  1587. gi.WriteByte (svc_temp_entity);
  1588. gi.WriteByte (te_beam);
  1589. gi.WriteShort (self - g_edicts);
  1590. gi.WritePosition (start);
  1591. gi.WritePosition (beam_endpt);
  1592. gi.multicast (self->s.origin, MULTICAST_ALL);
  1593. }
  1594. /*
  1595. =================
  1596. fire_heat
  1597. Fires a single heat beam. Zap.
  1598. =================
  1599. */
  1600. void fire_heat (edict_t *self, vec3_t start, vec3_t aimdir, vec3_t offset, int damage, int kick, qboolean monster)
  1601. {
  1602. if (monster)
  1603. fire_beams (self, start, aimdir, offset, damage, kick, TE_MONSTER_HEATBEAM, TE_HEATBEAM_SPARKS, MOD_HEATBEAM);
  1604. else
  1605. fire_beams (self, start, aimdir, offset, damage, kick, TE_HEATBEAM, TE_HEATBEAM_SPARKS, MOD_HEATBEAM);
  1606. }
  1607. #endif
  1608. // *************************
  1609. // BLASTER 2
  1610. // *************************
  1611. /*
  1612. =================
  1613. fire_blaster2
  1614. Fires a single green blaster bolt. Used by monsters, generally.
  1615. =================
  1616. */
  1617. void blaster2_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  1618. {
  1619. int mod;
  1620. int damagestat;
  1621. if (other == self->owner)
  1622. return;
  1623. if (surf && (surf->flags & SURF_SKY))
  1624. {
  1625. G_FreeEdict (self);
  1626. return;
  1627. }
  1628. if (self->owner && self->owner->client)
  1629. PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
  1630. if (other->takedamage)
  1631. {
  1632. // the only time players will be firing blaster2 bolts will be from the
  1633. // defender sphere.
  1634. if(self->owner->client)
  1635. mod = MOD_DEFENDER_SPHERE;
  1636. else
  1637. mod = MOD_BLASTER2;
  1638. if (self->owner)
  1639. {
  1640. damagestat = self->owner->takedamage;
  1641. self->owner->takedamage = DAMAGE_NO;
  1642. if (self->dmg >= 5)
  1643. T_RadiusDamage(self, self->owner, self->dmg*3, other, self->dmg_radius, 0);
  1644. T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod);
  1645. self->owner->takedamage = damagestat;
  1646. }
  1647. else
  1648. {
  1649. if (self->dmg >= 5)
  1650. T_RadiusDamage(self, self->owner, self->dmg*3, other, self->dmg_radius, 0);
  1651. T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod);
  1652. }
  1653. }
  1654. else
  1655. {
  1656. //PMM - yeowch this will get expensive
  1657. if (self->dmg >= 5)
  1658. T_RadiusDamage(self, self->owner, self->dmg*3, self->owner, self->dmg_radius, 0);
  1659. gi.WriteByte (svc_temp_entity);
  1660. gi.WriteByte (TE_BLASTER2);
  1661. gi.WritePosition (self->s.origin);
  1662. if (!plane)
  1663. gi.WriteDir (vec3_origin);
  1664. else
  1665. gi.WriteDir (plane->normal);
  1666. gi.multicast (self->s.origin, MULTICAST_PVS);
  1667. }
  1668. G_FreeEdict (self);
  1669. }
  1670. void fire_blaster2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper)
  1671. {
  1672. edict_t *bolt;
  1673. trace_t tr;
  1674. VectorNormalize (dir);
  1675. bolt = G_Spawn();
  1676. VectorCopy (start, bolt->s.origin);
  1677. VectorCopy (start, bolt->s.old_origin);
  1678. vectoangles2 (dir, bolt->s.angles);
  1679. VectorScale (dir, speed, bolt->velocity);
  1680. bolt->movetype = MOVETYPE_FLYMISSILE;
  1681. bolt->clipmask = MASK_SHOT;
  1682. bolt->solid = SOLID_BBOX;
  1683. bolt->s.effects |= effect;
  1684. VectorClear (bolt->mins);
  1685. VectorClear (bolt->maxs);
  1686. if (effect)
  1687. bolt->s.effects |= EF_TRACKER;
  1688. bolt->dmg_radius = 128;
  1689. bolt->s.modelindex = gi.modelindex ("models/proj/laser2/tris.md2");
  1690. bolt->touch = blaster2_touch;
  1691. bolt->owner = self;
  1692. bolt->nextthink = level.time + 2;
  1693. bolt->think = G_FreeEdict;
  1694. bolt->dmg = damage;
  1695. bolt->classname = "bolt";
  1696. gi.linkentity (bolt);
  1697. if (self->client)
  1698. check_dodge (self, bolt->s.origin, dir, speed);
  1699. tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
  1700. if (tr.fraction < 1.0)
  1701. {
  1702. VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
  1703. bolt->touch (bolt, tr.ent, NULL, NULL);
  1704. }
  1705. }
  1706. // *************************
  1707. // tracker
  1708. // *************************
  1709. /*
  1710. void tracker_boom_think (edict_t *self)
  1711. {
  1712. self->s.frame--;
  1713. if(self->s.frame < 0)
  1714. G_FreeEdict(self);
  1715. else
  1716. self->nextthink = level.time + 0.1;
  1717. }
  1718. void tracker_boom_spawn (vec3_t origin)
  1719. {
  1720. edict_t *boom;
  1721. boom = G_Spawn();
  1722. VectorCopy (origin, boom->s.origin);
  1723. boom->s.modelindex = gi.modelindex ("models/items/spawngro/tris.md2");
  1724. boom->s.skinnum = 1;
  1725. boom->s.frame = 2;
  1726. boom->classname = "tracker boom";
  1727. gi.linkentity (boom);
  1728. boom->think = tracker_boom_think;
  1729. boom->nextthink = level.time + 0.1;
  1730. //PMM
  1731. // boom->s.renderfx |= RF_TRANSLUCENT;
  1732. boom->s.effects |= EF_SPHERETRANS;
  1733. //pmm
  1734. }
  1735. */
  1736. #define TRACKER_DAMAGE_FLAGS (DAMAGE_NO_POWER_ARMOR | DAMAGE_ENERGY | DAMAGE_NO_KNOCKBACK)
  1737. #define TRACKER_IMPACT_FLAGS (DAMAGE_NO_POWER_ARMOR | DAMAGE_ENERGY)
  1738. #define TRACKER_DAMAGE_TIME 0.5 // seconds
  1739. void tracker_pain_daemon_think (edict_t *self)
  1740. {
  1741. static vec3_t pain_normal = { 0, 0, 1 };
  1742. int hurt;
  1743. if(!self->inuse)
  1744. return;
  1745. if((level.time - self->timestamp) > TRACKER_DAMAGE_TIME)
  1746. {
  1747. if(!self->enemy->client)
  1748. self->enemy->s.effects &= ~EF_TRACKERTRAIL;
  1749. G_FreeEdict (self);
  1750. }
  1751. else
  1752. {
  1753. if(self->enemy->health > 0)
  1754. {
  1755. // gi.dprintf("ouch %x\n", self);
  1756. T_Damage (self->enemy, self, self->owner, vec3_origin, self->enemy->s.origin, pain_normal,
  1757. self->dmg, 0, TRACKER_DAMAGE_FLAGS, MOD_TRACKER);
  1758. // if we kill the player, we'll be removed.
  1759. if(self->inuse)
  1760. {
  1761. // if we killed a monster, gib them.
  1762. if (self->enemy->health < 1)
  1763. {
  1764. if(self->enemy->gib_health)
  1765. hurt = - self->enemy->gib_health;
  1766. else
  1767. hurt = 500;
  1768. // gi.dprintf("non-player killed. ensuring gib! %d\n", hurt);
  1769. T_Damage (self->enemy, self, self->owner, vec3_origin, self->enemy->s.origin,
  1770. pain_normal, hurt, 0, TRACKER_DAMAGE_FLAGS, MOD_TRACKER);
  1771. }
  1772. if(self->enemy->client)
  1773. self->enemy->client->tracker_pain_framenum = level.framenum + 1;
  1774. else
  1775. self->enemy->s.effects |= EF_TRACKERTRAIL;
  1776. self->nextthink = level.time + FRAMETIME;
  1777. }
  1778. }
  1779. else
  1780. {
  1781. if(!self->enemy->client)
  1782. self->enemy->s.effects &= ~EF_TRACKERTRAIL;
  1783. G_FreeEdict (self);
  1784. }
  1785. }
  1786. }
  1787. void tracker_pain_daemon_spawn (edict_t *owner, edict_t *enemy, int damage)
  1788. {
  1789. edict_t *daemon;
  1790. if(enemy == NULL)
  1791. return;
  1792. daemon = G_Spawn();
  1793. daemon->classname = "pain daemon";
  1794. daemon->think = tracker_pain_daemon_think;
  1795. daemon->nextthink = level.time + FRAMETIME;
  1796. daemon->timestamp = level.time;
  1797. daemon->owner = owner;
  1798. daemon->enemy = enemy;
  1799. daemon->dmg = damage;
  1800. }
  1801. void tracker_explode (edict_t *self, cplane_t *plane)
  1802. {
  1803. vec3_t dir;
  1804. if(!plane)
  1805. VectorClear (dir);
  1806. else
  1807. VectorScale (plane->normal, 256, dir);
  1808. gi.WriteByte (svc_temp_entity);
  1809. gi.WriteByte (TE_TRACKER_EXPLOSION);
  1810. gi.WritePosition (self->s.origin);
  1811. gi.multicast (self->s.origin, MULTICAST_PVS);
  1812. // gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/disrupthit.wav"), 1, ATTN_NORM, 0);
  1813. // tracker_boom_spawn(self->s.origin);
  1814. G_FreeEdict (self);
  1815. }
  1816. void tracker_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  1817. {
  1818. float damagetime;
  1819. if (other == self->owner)
  1820. return;
  1821. if (surf && (surf->flags & SURF_SKY))
  1822. {
  1823. G_FreeEdict (self);
  1824. return;
  1825. }
  1826. if (self->client)
  1827. PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
  1828. if (other->takedamage)
  1829. {
  1830. if((other->svflags & SVF_MONSTER) || other->client)
  1831. {
  1832. if(other->health > 0) // knockback only for living creatures
  1833. {
  1834. // PMM - kickback was times 4 .. reduced to 3
  1835. // now this does no damage, just knockback
  1836. T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
  1837. /* self->dmg */ 0, (self->dmg*3), TRACKER_IMPACT_FLAGS, MOD_TRACKER);
  1838. if (!(other->flags & (FL_FLY|FL_SWIM)))
  1839. other->velocity[2] += 140;
  1840. damagetime = ((float)self->dmg)*FRAMETIME;
  1841. damagetime = damagetime / TRACKER_DAMAGE_TIME;
  1842. // gi.dprintf ("damage is %f\n", damagetime);
  1843. tracker_pain_daemon_spawn (self->owner, other, (int)damagetime);
  1844. }
  1845. else // lots of damage (almost autogib) for dead bodies
  1846. {
  1847. T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
  1848. self->dmg*4, (self->dmg*3), TRACKER_IMPACT_FLAGS, MOD_TRACKER);
  1849. }
  1850. }
  1851. else // full damage in one shot for inanimate objects
  1852. {
  1853. T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
  1854. self->dmg, (self->dmg*3), TRACKER_IMPACT_FLAGS, MOD_TRACKER);
  1855. }
  1856. }
  1857. tracker_explode (self, plane);
  1858. return;
  1859. }
  1860. void tracker_fly (edict_t *self)
  1861. {
  1862. vec3_t dest;
  1863. vec3_t dir;
  1864. vec3_t center;
  1865. if ((!self->enemy) || (!self->enemy->inuse) || (self->enemy->health < 1))
  1866. {
  1867. tracker_explode (self, NULL);
  1868. return;
  1869. }
  1870. /*
  1871. VectorCopy (self->enemy->s.origin, dest);
  1872. if(self->enemy->client)
  1873. dest[2] += self->enemy->viewheight;
  1874. */
  1875. // PMM - try to hunt for center of enemy, if possible and not client
  1876. if(self->enemy->client)
  1877. {
  1878. VectorCopy (self->enemy->s.origin, dest);
  1879. dest[2] += self->enemy->viewheight;
  1880. }
  1881. // paranoia
  1882. else if (VectorCompare(self->enemy->absmin, vec3_origin) || VectorCompare(self->enemy->absmax, vec3_origin))
  1883. {
  1884. VectorCopy (self->enemy->s.origin, dest);
  1885. }
  1886. else
  1887. {
  1888. VectorMA (vec3_origin, 0.5, self->enemy->absmin, center);
  1889. VectorMA (center, 0.5, self->enemy->absmax, center);
  1890. VectorCopy (center, dest);
  1891. }
  1892. VectorSubtract (dest, self->s.origin, dir);
  1893. VectorNormalize (dir);
  1894. vectoangles2 (dir, self->s.angles);
  1895. VectorScale (dir, self->speed, self->velocity);
  1896. VectorCopy(dest, self->monsterinfo.saved_goal);
  1897. self->nextthink = level.time + 0.1;
  1898. }
  1899. void fire_tracker (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, edict_t *enemy)
  1900. {
  1901. edict_t *bolt;
  1902. trace_t tr;
  1903. VectorNormalize (dir);
  1904. bolt = G_Spawn();
  1905. VectorCopy (start, bolt->s.origin);
  1906. VectorCopy (start, bolt->s.old_origin);
  1907. vectoangles2 (dir, bolt->s.angles);
  1908. VectorScale (dir, speed, bolt->velocity);
  1909. bolt->movetype = MOVETYPE_FLYMISSILE;
  1910. bolt->clipmask = MASK_SHOT;
  1911. bolt->solid = SOLID_BBOX;
  1912. bolt->speed = speed;
  1913. bolt->s.effects = EF_TRACKER;
  1914. bolt->s.sound = gi.soundindex ("weapons/disrupt.wav");
  1915. VectorClear (bolt->mins);
  1916. VectorClear (bolt->maxs);
  1917. bolt->s.modelindex = gi.modelindex ("models/proj/disintegrator/tris.md2");
  1918. bolt->touch = tracker_touch;
  1919. bolt->enemy = enemy;
  1920. bolt->owner = self;
  1921. bolt->dmg = damage;
  1922. bolt->classname = "tracker";
  1923. gi.linkentity (bolt);
  1924. if(enemy)
  1925. {
  1926. bolt->nextthink = level.time + 0.1;
  1927. bolt->think = tracker_fly;
  1928. }
  1929. else
  1930. {
  1931. bolt->nextthink = level.time + 10;
  1932. bolt->think = G_FreeEdict;
  1933. }
  1934. if (self->client)
  1935. check_dodge (self, bolt->s.origin, dir, speed);
  1936. tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
  1937. if (tr.fraction < 1.0)
  1938. {
  1939. VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
  1940. bolt->touch (bolt, tr.ent, NULL, NULL);
  1941. }
  1942. }