weapons.qc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503
  1. /* Copyright (C) 1996-2022 id Software LLC
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation; either version 2 of the License, or
  5. (at your option) any later version.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program; if not, write to the Free Software
  12. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  13. See file, 'COPYING', for details.
  14. */
  15. /*
  16. */
  17. void (entity targ, entity inflictor, entity attacker, float damage) T_Damage;
  18. void () player_run;
  19. void(entity bomb, entity attacker, float rad, entity ignore) T_RadiusDamage;
  20. void(vector org, vector vel, float damage) SpawnBlood;
  21. void() SuperDamageSound;
  22. // called by worldspawn
  23. void() W_Precache =
  24. {
  25. precache_sound ("weapons/r_exp3.wav"); // new rocket explosion
  26. precache_sound ("weapons/rocket1i.wav"); // spike gun
  27. precache_sound ("weapons/sgun1.wav");
  28. precache_sound ("weapons/guncock.wav"); // player shotgun
  29. precache_sound ("weapons/ric1.wav"); // ricochet (used in c code)
  30. precache_sound ("weapons/ric2.wav"); // ricochet (used in c code)
  31. precache_sound ("weapons/ric3.wav"); // ricochet (used in c code)
  32. precache_sound ("weapons/spike2.wav"); // super spikes
  33. precache_sound ("weapons/tink1.wav"); // spikes tink (used in c code)
  34. precache_sound ("weapons/grenade.wav"); // grenade launcher
  35. precache_sound ("weapons/bounce.wav"); // grenade bounce
  36. precache_sound ("weapons/shotgn2.wav"); // super shotgun
  37. };
  38. float() crandom =
  39. {
  40. return 2*(random() - 0.5);
  41. };
  42. /*
  43. ================
  44. W_FireAxe
  45. ================
  46. */
  47. void() W_FireAxe =
  48. {
  49. local vector source;
  50. local vector org;
  51. makevectors (self.v_angle);
  52. source = self.origin + '0 0 16';
  53. traceline (source, source + v_forward*64, FALSE, self);
  54. if (trace_fraction == 1.0)
  55. return;
  56. org = trace_endpos - v_forward*4;
  57. if (trace_ent.takedamage && trace_ent.classname != "monster_boss") // yoder mod, Jan 05 2021
  58. {
  59. trace_ent.axhitme = 1;
  60. SpawnBlood (org, '0 0 0', 20);
  61. T_Damage (trace_ent, self, self, 20);
  62. }
  63. else
  64. { // hit wall
  65. sound (self, CHAN_WEAPON, "player/axhit2.wav", 1, ATTN_NORM);
  66. WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  67. WriteByte (MSG_BROADCAST, TE_GUNSHOT);
  68. WriteCoord (MSG_BROADCAST, org_x);
  69. WriteCoord (MSG_BROADCAST, org_y);
  70. WriteCoord (MSG_BROADCAST, org_z);
  71. }
  72. };
  73. //============================================================================
  74. vector() wall_velocity =
  75. {
  76. local vector vel;
  77. vel = normalize (self.velocity);
  78. vel = normalize(vel + v_up*(random()- 0.5) + v_right*(random()- 0.5));
  79. vel = vel + 2*trace_plane_normal;
  80. vel = vel * 200;
  81. return vel;
  82. };
  83. /*
  84. ================
  85. SpawnMeatSpray
  86. ================
  87. */
  88. void(vector org, vector vel) SpawnMeatSpray =
  89. {
  90. local entity missile;
  91. missile = spawn ();
  92. missile.owner = self;
  93. missile.movetype = MOVETYPE_BOUNCE;
  94. missile.solid = SOLID_NOT;
  95. makevectors (self.angles);
  96. missile.velocity = vel;
  97. missile.velocity_z = missile.velocity_z + 250 + 50*random();
  98. missile.avelocity = '3000 1000 2000';
  99. // set missile duration
  100. missile.nextthink = time + 1;
  101. missile.think = SUB_Remove;
  102. setmodel (missile, "progs/zom_gib.mdl");
  103. setsize (missile, '0 0 0', '0 0 0');
  104. setorigin (missile, org);
  105. };
  106. /*
  107. ================
  108. SpawnBlood
  109. ================
  110. */
  111. void(vector org, vector vel, float damage) SpawnBlood =
  112. {
  113. particle (org, vel*0.1, 73, damage*2);
  114. };
  115. /*
  116. ================
  117. spawn_touchblood
  118. ================
  119. */
  120. void(float damage) spawn_touchblood =
  121. {
  122. local vector vel;
  123. vel = wall_velocity () * 0.2;
  124. SpawnBlood (self.origin + vel*0.01, vel, damage);
  125. };
  126. /*
  127. ================
  128. SpawnChunk
  129. ================
  130. */
  131. void(vector org, vector vel) SpawnChunk =
  132. {
  133. particle (org, vel*0.02, 0, 10);
  134. };
  135. /*
  136. ==============================================================================
  137. MULTI-DAMAGE
  138. Collects multiple small damages into a single damage
  139. ==============================================================================
  140. */
  141. entity multi_ent;
  142. float multi_damage;
  143. void() ClearMultiDamage =
  144. {
  145. multi_ent = world;
  146. multi_damage = 0;
  147. };
  148. void() ApplyMultiDamage =
  149. {
  150. if (!multi_ent)
  151. return;
  152. T_Damage (multi_ent, self, self, multi_damage);
  153. };
  154. void(entity hit, float damage) AddMultiDamage =
  155. {
  156. if (!hit)
  157. return;
  158. if (hit != multi_ent)
  159. {
  160. ApplyMultiDamage ();
  161. multi_damage = damage;
  162. multi_ent = hit;
  163. }
  164. else
  165. multi_damage = multi_damage + damage;
  166. };
  167. /*
  168. ==============================================================================
  169. BULLETS
  170. ==============================================================================
  171. */
  172. /*
  173. ================
  174. TraceAttack
  175. ================
  176. */
  177. void(float damage, vector dir) TraceAttack =
  178. {
  179. local vector vel, org;
  180. vel = normalize(dir + v_up*crandom() + v_right*crandom());
  181. vel = vel + 2*trace_plane_normal;
  182. vel = vel * 200;
  183. org = trace_endpos - dir*4;
  184. if (trace_ent.takedamage && trace_ent.classname != "monster_boss") // yoder mod, Jan 05 2021
  185. {
  186. SpawnBlood (org, vel*0.2, damage);
  187. AddMultiDamage (trace_ent, damage);
  188. }
  189. else
  190. {
  191. WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  192. WriteByte (MSG_BROADCAST, TE_GUNSHOT);
  193. WriteCoord (MSG_BROADCAST, org_x);
  194. WriteCoord (MSG_BROADCAST, org_y);
  195. WriteCoord (MSG_BROADCAST, org_z);
  196. }
  197. };
  198. /*
  199. ================
  200. FireBullets
  201. Used by shotgun, super shotgun, and enemy soldier firing
  202. Go to the trouble of combining multiple pellets into a single damage call.
  203. ================
  204. */
  205. void(float shotcount, vector dir, vector spread) FireBullets =
  206. {
  207. local vector direction;
  208. local vector src;
  209. makevectors(self.v_angle);
  210. src = self.origin + v_forward*10;
  211. src_z = self.absmin_z + self.size_z * 0.7;
  212. ClearMultiDamage ();
  213. while (shotcount > 0)
  214. {
  215. direction = dir + crandom()*spread_x*v_right + crandom()*spread_y*v_up;
  216. traceline (src, src + direction*2048, FALSE, self);
  217. if (trace_fraction != 1.0)
  218. TraceAttack (4, direction);
  219. shotcount = shotcount - 1;
  220. }
  221. ApplyMultiDamage ();
  222. };
  223. /*
  224. ================
  225. W_FireShotgun
  226. ================
  227. */
  228. void() W_FireShotgun =
  229. {
  230. local vector dir;
  231. sound (self, CHAN_WEAPON, "weapons/guncock.wav", 1, ATTN_NORM);
  232. self.punchangle_x = -2;
  233. self.currentammo = self.ammo_shells = self.ammo_shells - 1;
  234. dir = aim (self, 100000);
  235. FireBullets (6, dir, '0.04 0.04 0');
  236. };
  237. /*
  238. ================
  239. W_FireSuperShotgun
  240. ================
  241. */
  242. void() W_FireSuperShotgun =
  243. {
  244. local vector dir;
  245. if (self.currentammo == 1)
  246. {
  247. W_FireShotgun ();
  248. return;
  249. }
  250. sound (self ,CHAN_WEAPON, "weapons/shotgn2.wav", 1, ATTN_NORM);
  251. self.punchangle_x = -4;
  252. self.currentammo = self.ammo_shells = self.ammo_shells - 2;
  253. dir = aim (self, 100000);
  254. FireBullets (14, dir, '0.14 0.08 0');
  255. };
  256. /*
  257. ==============================================================================
  258. ROCKETS
  259. ==============================================================================
  260. */
  261. void() s_explode1 = [0, s_explode2] {};
  262. void() s_explode2 = [1, s_explode3] {};
  263. void() s_explode3 = [2, s_explode4] {};
  264. void() s_explode4 = [3, s_explode5] {};
  265. void() s_explode5 = [4, s_explode6] {};
  266. void() s_explode6 = [5, SUB_Remove] {};
  267. void() BecomeExplosion =
  268. {
  269. self.movetype = MOVETYPE_NONE;
  270. self.velocity = '0 0 0';
  271. self.touch = SUB_Null;
  272. setmodel (self, "progs/s_explod.spr");
  273. self.solid = SOLID_NOT;
  274. s_explode1 ();
  275. };
  276. void() T_MissileTouch =
  277. {
  278. local float damg;
  279. if (other == self.owner)
  280. return; // don't explode on owner
  281. if (pointcontents(self.origin) == CONTENT_SKY)
  282. {
  283. remove(self);
  284. return;
  285. }
  286. damg = 100 + random()*20;
  287. if (other.health)
  288. {
  289. if (other.classname == "monster_shambler")
  290. damg = damg * 0.5; // mostly immune
  291. T_Damage (other, self, self.owner, damg );
  292. }
  293. // don't do radius damage to the other, because all the damage
  294. // was done in the impact
  295. T_RadiusDamage (self, self.owner, 120, other);
  296. // sound (self, CHAN_WEAPON, "weapons/r_exp3.wav", 1, ATTN_NORM);
  297. self.origin = self.origin - 8*normalize(self.velocity);
  298. WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  299. WriteByte (MSG_BROADCAST, TE_EXPLOSION);
  300. WriteCoord (MSG_BROADCAST, self.origin_x);
  301. WriteCoord (MSG_BROADCAST, self.origin_y);
  302. WriteCoord (MSG_BROADCAST, self.origin_z);
  303. BecomeExplosion ();
  304. };
  305. /*
  306. ================
  307. W_FireRocket
  308. ================
  309. */
  310. void() W_FireRocket =
  311. {
  312. local entity missile;
  313. self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  314. sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);
  315. self.punchangle_x = -2;
  316. missile = spawn ();
  317. missile.owner = self;
  318. missile.movetype = MOVETYPE_FLYMISSILE;
  319. missile.solid = SOLID_BBOX;
  320. missile.classname = "missile";
  321. // set missile speed
  322. makevectors (self.v_angle);
  323. missile.velocity = aim(self, 1000);
  324. missile.velocity = missile.velocity * 1000;
  325. missile.angles = vectoangles(missile.velocity);
  326. missile.touch = T_MissileTouch;
  327. // set missile duration
  328. missile.nextthink = time + 5;
  329. missile.think = SUB_Remove;
  330. setmodel (missile, "progs/missile.mdl");
  331. setsize (missile, '0 0 0', '0 0 0');
  332. setorigin (missile, self.origin + v_forward*8 + '0 0 16');
  333. };
  334. /*
  335. ===============================================================================
  336. LIGHTNING
  337. ===============================================================================
  338. */
  339. /*
  340. =================
  341. LightningDamage
  342. =================
  343. */
  344. void(vector p1, vector p2, entity from, float damage) LightningDamage =
  345. {
  346. local entity e1, e2;
  347. local vector f;
  348. f = p2 - p1;
  349. normalize (f);
  350. f_x = 0 - f_y;
  351. f_y = f_x;
  352. f_z = 0;
  353. f = f*16;
  354. e1 = e2 = world;
  355. traceline (p1, p2, FALSE, self);
  356. if (trace_ent.takedamage)
  357. {
  358. particle (trace_endpos, '0 0 100', 225, damage*4);
  359. T_Damage (trace_ent, from, from, damage);
  360. if (self.classname == "player")
  361. {
  362. if (other.classname == "player")
  363. trace_ent.velocity_z = trace_ent.velocity_z + 400;
  364. }
  365. }
  366. e1 = trace_ent;
  367. traceline (p1 + f, p2 + f, FALSE, self);
  368. if (trace_ent != e1 && trace_ent.takedamage)
  369. {
  370. particle (trace_endpos, '0 0 100', 225, damage*4);
  371. T_Damage (trace_ent, from, from, damage);
  372. }
  373. e2 = trace_ent;
  374. traceline (p1 - f, p2 - f, FALSE, self);
  375. if (trace_ent != e1 && trace_ent != e2 && trace_ent.takedamage)
  376. {
  377. particle (trace_endpos, '0 0 100', 225, damage*4);
  378. T_Damage (trace_ent, from, from, damage);
  379. }
  380. };
  381. void() W_FireLightning =
  382. {
  383. local vector org;
  384. local float cells;
  385. if (self.ammo_cells < 1)
  386. {
  387. self.weapon = W_BestWeapon ();
  388. W_SetCurrentAmmo ();
  389. return;
  390. }
  391. // explode if under water
  392. if (self.waterlevel > 1)
  393. {
  394. cells = self.ammo_cells;
  395. self.ammo_cells = 0;
  396. W_SetCurrentAmmo ();
  397. T_RadiusDamage (self, self, 35*cells, world);
  398. return;
  399. }
  400. if (self.t_width < time)
  401. {
  402. sound (self, CHAN_WEAPON, "weapons/lhit.wav", 1, ATTN_NORM);
  403. self.t_width = time + 0.6;
  404. }
  405. self.punchangle_x = -2;
  406. self.currentammo = self.ammo_cells = self.ammo_cells - 1;
  407. org = self.origin + '0 0 16';
  408. traceline (org, org + v_forward*600, TRUE, self);
  409. WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  410. WriteByte (MSG_BROADCAST, TE_LIGHTNING2);
  411. WriteEntity (MSG_BROADCAST, self);
  412. WriteCoord (MSG_BROADCAST, org_x);
  413. WriteCoord (MSG_BROADCAST, org_y);
  414. WriteCoord (MSG_BROADCAST, org_z);
  415. WriteCoord (MSG_BROADCAST, trace_endpos_x);
  416. WriteCoord (MSG_BROADCAST, trace_endpos_y);
  417. WriteCoord (MSG_BROADCAST, trace_endpos_z);
  418. LightningDamage (self.origin, trace_endpos + v_forward*4, self, 30);
  419. };
  420. //=============================================================================
  421. void() GrenadeExplode =
  422. {
  423. T_RadiusDamage (self, self.owner, self.dmg, world);
  424. WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  425. WriteByte (MSG_BROADCAST, TE_EXPLOSION);
  426. WriteCoord (MSG_BROADCAST, self.origin_x);
  427. WriteCoord (MSG_BROADCAST, self.origin_y);
  428. WriteCoord (MSG_BROADCAST, self.origin_z);
  429. BecomeExplosion ();
  430. };
  431. void GrenadeThink()
  432. {
  433. self.cnt += 1;
  434. // Count to 50 at 20 ticks per second = 2.5 seconds until explosion
  435. if(self.cnt == 50)
  436. {
  437. GrenadeExplode();
  438. return;
  439. }
  440. float oldVel = vlen(self.oldorigin);
  441. float vel = vlen(self.velocity);
  442. if(vel == 0) //Stuck on downward slope?
  443. {
  444. if(oldVel > 120 && self.oldorigin_z < 0) //Were we moving downwards at a decent speed last tick?
  445. {
  446. // Trace straight down and find the normal of the surface it bounced off
  447. traceline(self.origin + '0 0 4', self.origin - '0 0 8', TRUE, self);
  448. if(trace_fraction < 1.0) // Hit something...
  449. {
  450. // dprint("reflected grenade bounce\n");
  451. // Reflect the previous velocity off that normal
  452. vector reflected = self.oldorigin - 2 * (self.oldorigin * trace_plane_normal) * trace_plane_normal;
  453. self.flags &~= FL_ONGROUND; //Remove FL_ONGROUND
  454. self.velocity = reflected * 0.6; // Scale it down a bit so it feels better
  455. self.avelocity = '400 400 400'; // Roll that sucker around
  456. }
  457. }
  458. }
  459. self.oldorigin = self.velocity;
  460. self.nextthink = time + 0.05;
  461. }
  462. void() GrenadeTouch =
  463. {
  464. if (other == self.owner)
  465. return; // don't explode on owner
  466. if (other.takedamage == DAMAGE_AIM)
  467. {
  468. GrenadeExplode();
  469. return;
  470. }
  471. if(self.velocity == '0 0 0')
  472. {
  473. self.avelocity = '0 0 0';
  474. }
  475. if(self.attack_finished < time)
  476. sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM); // bounce sound
  477. self.attack_finished = time + 0.1;
  478. };
  479. /*
  480. ================
  481. W_FireGrenade
  482. ================
  483. */
  484. void W_FireGrenade(float damage, vector direction);
  485. void W_FireGrenadeLauncher()
  486. {
  487. float damage = 120;
  488. makevectors (self.v_angle);
  489. vector velocity;
  490. if (self.v_angle_x)
  491. velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
  492. else
  493. {
  494. velocity = aim(self, 10000);
  495. velocity = velocity * 600;
  496. velocity_z = 200;
  497. }
  498. W_FireGrenade(damage, velocity) ;
  499. }
  500. void W_FireGrenade(float damage, vector direction)
  501. {
  502. local entity missile;
  503. self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  504. sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
  505. self.punchangle_x = -2;
  506. missile = spawn ();
  507. missile.owner = self;
  508. missile.movetype = MOVETYPE_BOUNCE;
  509. missile.solid = SOLID_BBOX;
  510. missile.classname = "grenade";
  511. missile.dmg = damage;
  512. // set missile speed
  513. missile.velocity = direction;
  514. missile.oldorigin = direction;
  515. missile.avelocity = '300 300 300';
  516. missile.angles = vectoangles(missile.velocity);
  517. missile.touch = GrenadeTouch;
  518. #ifdef GRENADE_BOUNCE_FIX
  519. // set missile duration
  520. missile.nextthink = time + 0.05;
  521. missile.think = GrenadeThink;
  522. #else
  523. // Uncomment this to go back to old grenade behaviour with no slope bounce fix.
  524. missile.nextthink = time + 2.5;
  525. missile.think = GrenadeExplode;
  526. #endif //GRENADE_BOUNCE_FIX
  527. setmodel (missile, "progs/grenade.mdl");
  528. setsize (missile, '0 0 0', '0 0 0');
  529. setorigin (missile, self.origin);
  530. };
  531. //=============================================================================
  532. void() spike_touch;
  533. void() superspike_touch;
  534. /*
  535. ===============
  536. launch_spike
  537. Used for both the player and the ogre
  538. ===============
  539. */
  540. void(vector org, vector dir) launch_spike =
  541. {
  542. newmis = spawn ();
  543. newmis.owner = self;
  544. newmis.movetype = MOVETYPE_FLYMISSILE;
  545. newmis.solid = SOLID_BBOX;
  546. newmis.angles = vectoangles(dir);
  547. newmis.touch = spike_touch;
  548. newmis.classname = "spike";
  549. newmis.think = SUB_Remove;
  550. newmis.nextthink = time + 6;
  551. setmodel (newmis, "progs/spike.mdl");
  552. setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
  553. setorigin (newmis, org);
  554. newmis.velocity = dir * 1000;
  555. };
  556. void() W_FireSuperSpikes =
  557. {
  558. local vector dir;
  559. sound (self, CHAN_WEAPON, "weapons/spike2.wav", 1, ATTN_NORM);
  560. self.attack_finished = time + 0.2;
  561. self.currentammo = self.ammo_nails = self.ammo_nails - 2;
  562. dir = aim (self, 1000);
  563. launch_spike (self.origin + '0 0 16', dir);
  564. newmis.touch = superspike_touch;
  565. setmodel (newmis, "progs/s_spike.mdl");
  566. setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
  567. self.punchangle_x = -2;
  568. };
  569. void(float ox) W_FireSpikes =
  570. {
  571. local vector dir;
  572. makevectors (self.v_angle);
  573. if (self.ammo_nails >= 2 && self.weapon == IT_SUPER_NAILGUN)
  574. {
  575. W_FireSuperSpikes ();
  576. return;
  577. }
  578. if (self.ammo_nails < 1)
  579. {
  580. self.weapon = W_BestWeapon ();
  581. W_SetCurrentAmmo ();
  582. return;
  583. }
  584. sound (self, CHAN_WEAPON, "weapons/rocket1i.wav", 1, ATTN_NORM);
  585. self.attack_finished = time + 0.2;
  586. self.currentammo = self.ammo_nails = self.ammo_nails - 1;
  587. dir = aim (self, 1000);
  588. launch_spike (self.origin + '0 0 16' + v_right*ox, dir);
  589. self.punchangle_x = -2;
  590. };
  591. .float hit_z;
  592. void() spike_touch =
  593. {
  594. if (other == self.owner)
  595. return;
  596. if (other.solid == SOLID_TRIGGER)
  597. return; // trigger field, do nothing
  598. if (pointcontents(self.origin) == CONTENT_SKY)
  599. {
  600. remove(self);
  601. return;
  602. }
  603. // hit something that bleeds
  604. if (other.takedamage && other.classname != "monster_boss") // yoder mod, jan 05 2021
  605. {
  606. spawn_touchblood (9);
  607. T_Damage (other, self, self.owner, 9);
  608. }
  609. else
  610. {
  611. WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  612. if (self.classname == "wizspike")
  613. WriteByte (MSG_BROADCAST, TE_WIZSPIKE);
  614. else if (self.classname == "knightspike")
  615. WriteByte (MSG_BROADCAST, TE_KNIGHTSPIKE);
  616. else
  617. WriteByte (MSG_BROADCAST, TE_SPIKE);
  618. WriteCoord (MSG_BROADCAST, self.origin_x);
  619. WriteCoord (MSG_BROADCAST, self.origin_y);
  620. WriteCoord (MSG_BROADCAST, self.origin_z);
  621. }
  622. remove(self);
  623. };
  624. void() superspike_touch =
  625. {
  626. if (other == self.owner)
  627. return;
  628. if (other.solid == SOLID_TRIGGER)
  629. return; // trigger field, do nothing
  630. if (pointcontents(self.origin) == CONTENT_SKY)
  631. {
  632. remove(self);
  633. return;
  634. }
  635. // hit something that bleeds
  636. if (other.takedamage && other.classname != "monster_boss") // yoder mod, Jan 05 2021
  637. {
  638. spawn_touchblood (18);
  639. T_Damage (other, self, self.owner, 18);
  640. }
  641. else
  642. {
  643. WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  644. WriteByte (MSG_BROADCAST, TE_SUPERSPIKE);
  645. WriteCoord (MSG_BROADCAST, self.origin_x);
  646. WriteCoord (MSG_BROADCAST, self.origin_y);
  647. WriteCoord (MSG_BROADCAST, self.origin_z);
  648. }
  649. remove(self);
  650. };
  651. /*
  652. ===============================================================================
  653. PLAYER WEAPON USE
  654. ===============================================================================
  655. */
  656. void() W_SetCurrentAmmo =
  657. {
  658. player_run (); // get out of any weapon firing states
  659. self.items = self.items - ( self.items & (IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS) );
  660. if (self.weapon == IT_AXE)
  661. {
  662. self.currentammo = 0;
  663. self.weaponmodel = "progs/v_axe.mdl";
  664. self.weaponframe = 0;
  665. }
  666. else if (self.weapon == IT_SHOTGUN)
  667. {
  668. self.currentammo = self.ammo_shells;
  669. self.weaponmodel = "progs/v_shot.mdl";
  670. self.weaponframe = 0;
  671. self.items = self.items | IT_SHELLS;
  672. }
  673. else if (self.weapon == IT_SUPER_SHOTGUN)
  674. {
  675. self.currentammo = self.ammo_shells;
  676. self.weaponmodel = "progs/v_shot2.mdl";
  677. self.weaponframe = 0;
  678. self.items = self.items | IT_SHELLS;
  679. }
  680. else if (self.weapon == IT_NAILGUN)
  681. {
  682. self.currentammo = self.ammo_nails;
  683. self.weaponmodel = "progs/v_nail.mdl";
  684. self.weaponframe = 0;
  685. self.items = self.items | IT_NAILS;
  686. }
  687. else if (self.weapon == IT_SUPER_NAILGUN)
  688. {
  689. self.currentammo = self.ammo_nails;
  690. self.weaponmodel = "progs/v_nail2.mdl";
  691. self.weaponframe = 0;
  692. self.items = self.items | IT_NAILS;
  693. }
  694. else if (self.weapon == IT_GRENADE_LAUNCHER)
  695. {
  696. self.currentammo = self.ammo_rockets;
  697. self.weaponmodel = "progs/v_rock.mdl";
  698. self.weaponframe = 0;
  699. self.items = self.items | IT_ROCKETS;
  700. }
  701. else if (self.weapon == IT_ROCKET_LAUNCHER)
  702. {
  703. self.currentammo = self.ammo_rockets;
  704. self.weaponmodel = "progs/v_rock2.mdl";
  705. self.weaponframe = 0;
  706. self.items = self.items | IT_ROCKETS;
  707. }
  708. else if (self.weapon == IT_LIGHTNING)
  709. {
  710. self.currentammo = self.ammo_cells;
  711. self.weaponmodel = "progs/v_light.mdl";
  712. self.weaponframe = 0;
  713. self.items = self.items | IT_CELLS;
  714. }
  715. else
  716. {
  717. self.currentammo = 0;
  718. self.weaponmodel = string_null;
  719. self.weaponframe = 0;
  720. }
  721. };
  722. float() W_BestWeapon =
  723. {
  724. local float it;
  725. it = self.items;
  726. if (self.waterlevel <= 1 && self.ammo_cells >= 1 && (it & IT_LIGHTNING) )
  727. return IT_LIGHTNING;
  728. if(self.ammo_nails >= 2 && (it & IT_SUPER_NAILGUN) )
  729. return IT_SUPER_NAILGUN;
  730. if(self.ammo_shells >= 2 && (it & IT_SUPER_SHOTGUN) )
  731. return IT_SUPER_SHOTGUN;
  732. if(self.ammo_nails >= 1 && (it & IT_NAILGUN) )
  733. return IT_NAILGUN;
  734. if(self.ammo_shells >= 1 && (it & IT_SHOTGUN) )
  735. return IT_SHOTGUN;
  736. if(it & IT_AXE)
  737. return IT_AXE;
  738. //No weapon
  739. return 0;
  740. };
  741. float(entity playerEnt, float old, float new) W_WantsToChangeWeapon =
  742. {
  743. float playerFlags = CheckPlayerEXFlags(playerEnt);
  744. if((playerFlags & PEF_CHANGENEVER) != 0)
  745. {
  746. return 0;
  747. }
  748. if(((playerFlags & PEF_CHANGEONLYNEW) != 0) && old == new)
  749. {
  750. return 0;
  751. }
  752. return 1;
  753. };
  754. float() W_CheckNoAmmo =
  755. {
  756. if (self.currentammo > 0)
  757. return TRUE;
  758. if (self.weapon == IT_AXE)
  759. return TRUE;
  760. self.weapon = W_BestWeapon ();
  761. W_SetCurrentAmmo ();
  762. // drop the weapon down
  763. return FALSE;
  764. };
  765. /*
  766. ============
  767. W_Attack
  768. An attack impulse can be triggered now
  769. ============
  770. */
  771. void() player_axe1;
  772. void() player_axeb1;
  773. void() player_axec1;
  774. void() player_axed1;
  775. void() player_shot1;
  776. void() player_nail1;
  777. void() player_light1;
  778. void() player_rocket1;
  779. void() W_Attack =
  780. {
  781. local float r;
  782. if (!self.weapon)
  783. return;
  784. if (!W_CheckNoAmmo ())
  785. return;
  786. makevectors (self.v_angle); // calculate forward angle for velocity
  787. self.show_hostile = time + 1; // wake monsters up
  788. if (self.weapon == IT_AXE)
  789. {
  790. sound (self, CHAN_WEAPON, "weapons/ax1.wav", 1, ATTN_NORM);
  791. r = random();
  792. if(cvar("horde") && (serverflags & SIGIL_E3)) // horde rune behavior
  793. {
  794. if ((self.axe_hit_chain >= 2) && (time < self.axe_hit_chain_time))
  795. {
  796. dprint("vertical chop: ");
  797. // vertical CHOP
  798. if (r < 0.5)
  799. {
  800. dprint(" axe1\n");
  801. player_axe1 ();
  802. }
  803. else
  804. {
  805. dprint(" axec1\n");
  806. player_axec1 ();
  807. }
  808. self.attack_finished = time + 0.8; // long recover
  809. }
  810. else
  811. {
  812. dprint("diagonal sweep:");
  813. // diagonal SWEEP
  814. if (r < 0.5)
  815. {
  816. dprint(" axeb1\n");
  817. player_axeb1 ();
  818. }
  819. else
  820. {
  821. dprint(" axed1\n");
  822. player_axed1 ();
  823. }
  824. if (self.axe_hit_chain > 1)
  825. self.attack_finished = time + 0.6;
  826. else
  827. self.attack_finished = time + 0.4;
  828. }
  829. }
  830. else // normal behavior
  831. {
  832. r = random();
  833. if (r < 0.25)
  834. {
  835. dprint("axe1\n");
  836. player_axe1 ();
  837. }
  838. else if (r<0.5)
  839. {
  840. dprint("axeb1\n");
  841. player_axeb1 ();
  842. }
  843. else if (r<0.75)
  844. {
  845. dprint("axec1\n");
  846. player_axec1 ();
  847. }
  848. else
  849. {
  850. dprint("axed1\n");
  851. player_axed1 ();
  852. }
  853. self.attack_finished = time + 0.5;
  854. }
  855. }
  856. else if (self.weapon == IT_SHOTGUN)
  857. {
  858. player_shot1 ();
  859. W_FireShotgun ();
  860. self.attack_finished = time + 0.5;
  861. }
  862. else if (self.weapon == IT_SUPER_SHOTGUN)
  863. {
  864. player_shot1 ();
  865. W_FireSuperShotgun ();
  866. self.attack_finished = time + 0.7;
  867. }
  868. else if (self.weapon == IT_NAILGUN)
  869. {
  870. player_nail1 ();
  871. }
  872. else if (self.weapon == IT_SUPER_NAILGUN)
  873. {
  874. player_nail1 ();
  875. }
  876. else if (self.weapon == IT_GRENADE_LAUNCHER)
  877. {
  878. player_rocket1();
  879. W_FireGrenadeLauncher();
  880. self.attack_finished = time + 0.6;
  881. }
  882. else if (self.weapon == IT_ROCKET_LAUNCHER)
  883. {
  884. player_rocket1();
  885. W_FireRocket();
  886. self.attack_finished = time + 0.8;
  887. }
  888. else if (self.weapon == IT_LIGHTNING)
  889. {
  890. player_light1();
  891. self.attack_finished = time + 0.1;
  892. sound (self, CHAN_AUTO, "weapons/lstart.wav", 1, ATTN_NORM);
  893. }
  894. };
  895. /*
  896. ============
  897. W_ChangeWeapon
  898. ============
  899. */
  900. void() W_ChangeWeapon =
  901. {
  902. local float it, am, fl;
  903. it = self.items;
  904. am = 0;
  905. if (self.impulse == 1)
  906. {
  907. fl = IT_AXE;
  908. }
  909. else if (self.impulse == 2)
  910. {
  911. fl = IT_SHOTGUN;
  912. if (self.ammo_shells < 1)
  913. am = 1;
  914. }
  915. else if (self.impulse == 3)
  916. {
  917. fl = IT_SUPER_SHOTGUN;
  918. if (self.ammo_shells < 2)
  919. am = 1;
  920. }
  921. else if (self.impulse == 4)
  922. {
  923. fl = IT_NAILGUN;
  924. if (self.ammo_nails < 1)
  925. am = 1;
  926. }
  927. else if (self.impulse == 5)
  928. {
  929. fl = IT_SUPER_NAILGUN;
  930. if (self.ammo_nails < 2)
  931. am = 1;
  932. }
  933. else if (self.impulse == 6)
  934. {
  935. fl = IT_GRENADE_LAUNCHER;
  936. if (self.ammo_rockets < 1)
  937. am = 1;
  938. }
  939. else if (self.impulse == 7)
  940. {
  941. fl = IT_ROCKET_LAUNCHER;
  942. if (self.ammo_rockets < 1)
  943. am = 1;
  944. }
  945. else if (self.impulse == 8)
  946. {
  947. fl = IT_LIGHTNING;
  948. if (self.ammo_cells < 1)
  949. am = 1;
  950. }
  951. self.impulse = 0;
  952. if (!(self.items & fl))
  953. { // don't have the weapon or the ammo
  954. sprint (self, "$qc_no_weapon");
  955. return;
  956. }
  957. if (am)
  958. { // don't have the ammo
  959. sprint (self, "$qc_not_enough_ammo");
  960. return;
  961. }
  962. //
  963. // set weapon, set ammo
  964. //
  965. self.weapon = fl;
  966. W_SetCurrentAmmo ();
  967. };
  968. /*
  969. ============
  970. CheatCommand
  971. ============
  972. */
  973. void(float NoKeys = FALSE) CheatCommand =
  974. {
  975. if ( ( deathmatch || coop ) && cheats_allowed == 0 ) { // mal: allow impulse 9 IF cheats allowed
  976. return;
  977. }
  978. self.ammo_rockets = 100;
  979. self.ammo_nails = 200;
  980. self.ammo_shells = 100;
  981. if(NoKeys)
  982. {
  983. self.items = self.items |
  984. IT_AXE |
  985. IT_SHOTGUN |
  986. IT_SUPER_SHOTGUN |
  987. IT_NAILGUN |
  988. IT_SUPER_NAILGUN |
  989. IT_GRENADE_LAUNCHER |
  990. IT_ROCKET_LAUNCHER;
  991. }
  992. else
  993. {
  994. self.items = self.items |
  995. IT_AXE |
  996. IT_SHOTGUN |
  997. IT_SUPER_SHOTGUN |
  998. IT_NAILGUN |
  999. IT_SUPER_NAILGUN |
  1000. IT_GRENADE_LAUNCHER |
  1001. IT_ROCKET_LAUNCHER |
  1002. IT_KEY1 | IT_KEY2;
  1003. }
  1004. self.ammo_cells = 200;
  1005. self.items = self.items | IT_LIGHTNING;
  1006. self.weapon = IT_ROCKET_LAUNCHER;
  1007. self.impulse = 0;
  1008. W_SetCurrentAmmo ();
  1009. };
  1010. /*
  1011. ============
  1012. CycleWeaponCommand
  1013. Go to the next weapon with ammo
  1014. ============
  1015. */
  1016. void() CycleWeaponCommand =
  1017. {
  1018. local float it, am, wep;
  1019. it = self.items;
  1020. wep = self.weapon;
  1021. self.impulse = 0;
  1022. float cnt = 10;
  1023. while (cnt)
  1024. {
  1025. am = 0;
  1026. if(!wep)
  1027. {
  1028. wep = IT_AXE;
  1029. }
  1030. else if (wep == IT_LIGHTNING)
  1031. {
  1032. wep = IT_AXE;
  1033. }
  1034. else if (wep == IT_AXE)
  1035. {
  1036. wep = IT_SHOTGUN;
  1037. if (self.ammo_shells < 1)
  1038. am = 1;
  1039. }
  1040. else if (wep == IT_SHOTGUN)
  1041. {
  1042. wep = IT_SUPER_SHOTGUN;
  1043. if (self.ammo_shells < 2)
  1044. am = 1;
  1045. }
  1046. else if (wep == IT_SUPER_SHOTGUN)
  1047. {
  1048. wep = IT_NAILGUN;
  1049. if (self.ammo_nails < 1)
  1050. am = 1;
  1051. }
  1052. else if (wep == IT_NAILGUN)
  1053. {
  1054. wep = IT_SUPER_NAILGUN;
  1055. if (self.ammo_nails < 2)
  1056. am = 1;
  1057. }
  1058. else if (wep == IT_SUPER_NAILGUN)
  1059. {
  1060. wep = IT_GRENADE_LAUNCHER;
  1061. if (self.ammo_rockets < 1)
  1062. am = 1;
  1063. }
  1064. else if (wep == IT_GRENADE_LAUNCHER)
  1065. {
  1066. wep = IT_ROCKET_LAUNCHER;
  1067. if (self.ammo_rockets < 1)
  1068. am = 1;
  1069. }
  1070. else if (wep == IT_ROCKET_LAUNCHER)
  1071. {
  1072. wep = IT_LIGHTNING;
  1073. if (self.ammo_cells < 1)
  1074. am = 1;
  1075. }
  1076. if ( (it & wep) && am == 0)
  1077. {
  1078. self.weapon = wep;
  1079. W_SetCurrentAmmo ();
  1080. return;
  1081. }
  1082. cnt--;
  1083. }
  1084. };
  1085. /*
  1086. ============
  1087. CycleWeaponReverseCommand
  1088. Go to the prev weapon with ammo
  1089. ============
  1090. */
  1091. void() CycleWeaponReverseCommand =
  1092. {
  1093. local float it, am, wep;
  1094. it = self.items;
  1095. wep = self.weapon;
  1096. self.impulse = 0;
  1097. float cnt = 10;
  1098. while (cnt)
  1099. {
  1100. am = 0;
  1101. if(!wep)
  1102. {
  1103. wep = IT_LIGHTNING;
  1104. }
  1105. else if (wep == IT_LIGHTNING)
  1106. {
  1107. wep = IT_ROCKET_LAUNCHER;
  1108. if (self.ammo_rockets < 1)
  1109. am = 1;
  1110. }
  1111. else if (wep == IT_ROCKET_LAUNCHER)
  1112. {
  1113. wep = IT_GRENADE_LAUNCHER;
  1114. if (self.ammo_rockets < 1)
  1115. am = 1;
  1116. }
  1117. else if (wep == IT_GRENADE_LAUNCHER)
  1118. {
  1119. wep = IT_SUPER_NAILGUN;
  1120. if (self.ammo_nails < 2)
  1121. am = 1;
  1122. }
  1123. else if (wep == IT_SUPER_NAILGUN)
  1124. {
  1125. wep = IT_NAILGUN;
  1126. if (self.ammo_nails < 1)
  1127. am = 1;
  1128. }
  1129. else if (wep == IT_NAILGUN)
  1130. {
  1131. wep = IT_SUPER_SHOTGUN;
  1132. if (self.ammo_shells < 2)
  1133. am = 1;
  1134. }
  1135. else if (wep == IT_SUPER_SHOTGUN)
  1136. {
  1137. wep = IT_SHOTGUN;
  1138. if (self.ammo_shells < 1)
  1139. am = 1;
  1140. }
  1141. else if (wep == IT_SHOTGUN)
  1142. {
  1143. wep = IT_AXE;
  1144. }
  1145. else if (wep == IT_AXE)
  1146. {
  1147. wep = IT_LIGHTNING;
  1148. if (self.ammo_cells < 1)
  1149. am = 1;
  1150. }
  1151. if ( (it & wep) && am == 0)
  1152. {
  1153. self.weapon = wep;
  1154. W_SetCurrentAmmo ();
  1155. return;
  1156. }
  1157. cnt--;
  1158. }
  1159. };
  1160. /*
  1161. ============
  1162. ServerflagsCommand
  1163. Just for development
  1164. ============
  1165. */
  1166. void() ServerflagsCommand =
  1167. {
  1168. serverflags = serverflags * 2 + 1;
  1169. };
  1170. void() QuadCheat =
  1171. {
  1172. if (deathmatch || coop)
  1173. return;
  1174. self.super_time = 1;
  1175. self.super_damage_finished = time + 30;
  1176. self.items = self.items | IT_QUAD;
  1177. dprint ("quad cheat\n");
  1178. };
  1179. void() Omnicide =
  1180. {
  1181. entity oself = self;
  1182. self = nextent(world);
  1183. while(self)
  1184. {
  1185. if(self.flags & (FL_MONSTER | FL_FUTUREMONSTER))
  1186. {
  1187. if(self.target || self.killtarget)
  1188. {
  1189. SUB_UseTargets();
  1190. }
  1191. SUB_Remove();
  1192. }
  1193. self = nextent(self);
  1194. }
  1195. self = oself;
  1196. killed_monsters = total_monsters;
  1197. WriteByte (MSG_ALL, SVC_UPDATESTAT);
  1198. WriteByte(MSG_ALL, 14); // 14 = STAT_MONSTERS
  1199. WriteLong(MSG_ALL, total_monsters);
  1200. }
  1201. /*
  1202. ============
  1203. ImpulseCommands
  1204. ============
  1205. */
  1206. void() ImpulseCommands =
  1207. {
  1208. if (self.impulse >= 1 && self.impulse <= 8)
  1209. W_ChangeWeapon ();
  1210. else if (self.impulse == 9)
  1211. CheatCommand ();
  1212. else if (self.impulse == 99)
  1213. CheatCommand (TRUE);
  1214. else if (self.impulse == 10)
  1215. CycleWeaponCommand ();
  1216. else if (self.impulse == 11)
  1217. ServerflagsCommand ();
  1218. else if (self.impulse == 12)
  1219. CycleWeaponReverseCommand ();
  1220. else if (self.impulse == 255)
  1221. QuadCheat ();
  1222. else if (self.impulse == 219)
  1223. Omnicide ();
  1224. self.impulse = 0;
  1225. };
  1226. /*
  1227. ============
  1228. W_WeaponFrame
  1229. Called every frame so impulse events can be handled as well as possible
  1230. ============
  1231. */
  1232. void() W_WeaponFrame =
  1233. {
  1234. if (time < self.attack_finished)
  1235. return;
  1236. ImpulseCommands ();
  1237. // check for attack
  1238. if (self.button0)
  1239. {
  1240. SuperDamageSound ();
  1241. W_Attack ();
  1242. }
  1243. };
  1244. /*
  1245. ========
  1246. SuperDamageSound
  1247. Plays sound if needed
  1248. ========
  1249. */
  1250. void() SuperDamageSound =
  1251. {
  1252. if (self.super_damage_finished > time)
  1253. {
  1254. if (self.super_sound < time)
  1255. {
  1256. self.super_sound = time + 1;
  1257. sound (self, CHAN_BODY, "items/damage3.wav", 1, ATTN_NORM);
  1258. }
  1259. }
  1260. return;
  1261. };