misc.qc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  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. /*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)
  16. Used as a positional target for spotlights, etc.
  17. */
  18. void() info_null =
  19. {
  20. remove(self);
  21. };
  22. /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
  23. Used as a positional target for lightning.
  24. */
  25. void() info_notnull =
  26. {
  27. };
  28. //============================================================================
  29. /*QUAKED misc_fireball (0 .5 .8) (-8 -8 -8) (8 8 8)
  30. Lava Balls
  31. */
  32. void() fire_fly;
  33. void() fire_touch;
  34. void() misc_fireball =
  35. {
  36. if (!self.speed)
  37. {
  38. self.speed = 1000;
  39. }
  40. if(self.noise)
  41. {
  42. precache_sound(self.noise);
  43. }
  44. if(self.noise2)
  45. {
  46. precache_sound(self.noise2);
  47. }
  48. precache_model ("progs/lavaball.mdl");
  49. self.classname = "fireball";
  50. if(self.targetname)
  51. {
  52. self.use = fire_fly;
  53. }
  54. else
  55. {
  56. self.nextthink = time + (random() * 5);
  57. self.think = fire_fly;
  58. }
  59. };
  60. void() fire_fly =
  61. {
  62. local entity fireball;
  63. fireball = spawn();
  64. fireball.solid = SOLID_TRIGGER;
  65. fireball.movetype = MOVETYPE_TOSS;
  66. if(self.movedir)
  67. {
  68. fireball.velocity = self.movedir;
  69. fireball.velocity_x += crandom() * 20;
  70. fireball.velocity_y += crandom() * 20;
  71. fireball.velocity_z += crandom() * 20;
  72. }
  73. else
  74. {
  75. fireball.velocity_x = (random() * 100) - 50;
  76. fireball.velocity_y = (random() * 100) - 50;
  77. fireball.velocity_z = self.speed + (random() * 200);
  78. }
  79. fireball.classname = "fireball";
  80. setmodel (fireball, "progs/lavaball.mdl");
  81. setsize (fireball, '0 0 0', '0 0 0');
  82. setorigin (fireball, self.origin);
  83. fireball.think = SUB_Remove;
  84. fireball.touch = fire_touch;
  85. if(self.noise)
  86. {
  87. sound (self, CHAN_VOICE, self.noise, 1.0, ATTN_NORM);
  88. }
  89. fireball.noise2 = self.noise2;
  90. self.nextthink = time + (random() * 5) + 3;
  91. self.think = fire_fly;
  92. };
  93. void() fire_touch =
  94. {
  95. T_Damage (other, self, self, 20);
  96. if(self.noise2)
  97. {
  98. sound (self, CHAN_VOICE, self.noise2, 0.8, ATTN_NORM);
  99. }
  100. remove(self);
  101. };
  102. //============================================================================
  103. void() barrel_explode =
  104. {
  105. self.origin_z = self.origin_z + 32;
  106. // did say self.owner
  107. T_RadiusDamage (self, self, 160, world);
  108. sound (self, CHAN_VOICE, "weapons/r_exp3.wav", 1, ATTN_NORM);
  109. particle (self.origin, '0 0 0', 75, 255);
  110. // yoder add, 27/09/2020 to make barrels fire targets on explode
  111. activator = self.enemy;
  112. dprint("enemy name: ");
  113. dprint(self.enemy.classname);
  114. dprint("\n");
  115. SUB_UseTargets ();
  116. BecomeExplosion ();
  117. };
  118. void() barrel_detonate =
  119. {
  120. self.takedamage = DAMAGE_NO;
  121. self.think = barrel_explode;
  122. self.nextthink = self.ltime + 0.15;
  123. }
  124. /*QUAKED misc_explobox (0 .5 .8) (0 0 0) (32 32 64)
  125. */
  126. void() misc_explobox =
  127. {
  128. local float oldz;
  129. if(!self.mdl) self.mdl = "maps/b_explob.bsp";
  130. self.solid = SOLID_BSP;
  131. self.movetype = MOVETYPE_PUSH;
  132. precache_model (self.mdl);
  133. setmodel (self, self.mdl);
  134. precache_sound ("weapons/r_exp3.wav");
  135. self.health = 20;
  136. self.th_die = barrel_detonate;
  137. self.takedamage = DAMAGE_AIM;
  138. self.origin_z = self.origin_z + 2;
  139. oldz = self.origin_z;
  140. droptofloor();
  141. if (oldz - self.origin_z > 250)
  142. {
  143. dprint ("explobox fell out of level at ");
  144. dprint (vtos(self.origin));
  145. dprint ("\n");
  146. remove(self);
  147. }
  148. self.classname = "explo_box"; // Used by ClientObituary to print death message
  149. };
  150. /*QUAKED misc_explobox2 (0 .5 .8) (0 0 0) (32 32 64)
  151. Smaller exploding box, REGISTERED ONLY
  152. */
  153. void() misc_explobox2 =
  154. {
  155. self.mdl = "maps/b_exbox2.bsp";
  156. misc_explobox();
  157. };
  158. //============================================================================
  159. void func_explode_detonate()
  160. {
  161. vector pos = self.mins + (self.size * 0.5);
  162. pos_z -= 32;
  163. setmodel(self, string_null);
  164. setorigin(self, pos);
  165. barrel_explode();
  166. }
  167. void() func_explode_die =
  168. {
  169. self.takedamage = DAMAGE_NO;
  170. self.think = func_explode_detonate;
  171. self.nextthink = self.ltime + 0.15;
  172. }
  173. /*QUAKED func_explode (0 .5 .8) (0 0 0) (32 32 64)
  174. Custom exploding box
  175. */
  176. void func_explode() =
  177. {
  178. self.angles = '0 0 0';
  179. self.movetype = MOVETYPE_PUSH;
  180. self.solid = SOLID_BSP;
  181. setmodel (self, self.model);
  182. precache_sound ("weapons/r_exp3.wav");
  183. self.health = 20;
  184. self.th_die = func_explode_die;
  185. self.takedamage = DAMAGE_AIM;
  186. };
  187. //============================================================================
  188. float SPAWNFLAG_SUPERSPIKE = 1;
  189. float SPAWNFLAG_LASER = 2;
  190. void(vector org, vector vec) LaunchLaser;
  191. void() spikeshooter_use =
  192. {
  193. if (self.spawnflags & SPAWNFLAG_LASER)
  194. {
  195. sound (self, CHAN_VOICE, "enforcer/enfire.wav", 1, ATTN_NORM);
  196. LaunchLaser (self.origin, self.movedir);
  197. }
  198. else
  199. {
  200. sound (self, CHAN_VOICE, "weapons/spike2.wav", 1, ATTN_NORM);
  201. launch_spike (self.origin, self.movedir);
  202. newmis.velocity = self.movedir * 500;
  203. if (self.spawnflags & SPAWNFLAG_SUPERSPIKE)
  204. newmis.touch = superspike_touch;
  205. }
  206. };
  207. void() shooter_think =
  208. {
  209. spikeshooter_use ();
  210. self.nextthink = time + self.wait;
  211. newmis.velocity = self.movedir * 500;
  212. };
  213. /*QUAKED trap_spikeshooter (0 .5 .8) (-8 -8 -8) (8 8 8) superspike laser
  214. When triggered, fires a spike in the direction set in QuakeEd.
  215. Laser is only for REGISTERED.
  216. */
  217. void() trap_spikeshooter =
  218. {
  219. SetMovedir ();
  220. self.use = spikeshooter_use;
  221. if (self.spawnflags & SPAWNFLAG_LASER)
  222. {
  223. precache_model2 ("progs/laser.mdl");
  224. precache_sound2 ("enforcer/enfire.wav");
  225. precache_sound2 ("enforcer/enfstop.wav");
  226. }
  227. else
  228. precache_sound ("weapons/spike2.wav");
  229. };
  230. /*QUAKED trap_shooter (0 .5 .8) (-8 -8 -8) (8 8 8) superspike laser
  231. Continuously fires spikes.
  232. "wait" time between spike (1.0 default)
  233. "nextthink" delay before firing first spike, so multiple shooters can be stagered.
  234. */
  235. void() trap_shooter =
  236. {
  237. trap_spikeshooter ();
  238. if (self.wait == 0)
  239. self.wait = 1;
  240. self.nextthink = self.nextthink + self.wait + self.ltime;
  241. self.think = shooter_think;
  242. };
  243. /*
  244. ===============================================================================
  245. ===============================================================================
  246. */
  247. void() make_bubbles;
  248. void() bubble_remove;
  249. void() bubble_bob;
  250. /*QUAKED air_bubbles (0 .5 .8) (-8 -8 -8) (8 8 8)
  251. testing air bubbles
  252. */
  253. void() air_bubbles =
  254. {
  255. if (deathmatch)
  256. {
  257. remove (self);
  258. return;
  259. }
  260. precache_model ("progs/s_bubble.spr");
  261. self.nextthink = time + 1;
  262. self.think = make_bubbles;
  263. };
  264. void() make_bubbles =
  265. {
  266. local entity bubble;
  267. bubble = spawn();
  268. setmodel (bubble, "progs/s_bubble.spr");
  269. setorigin (bubble, self.origin);
  270. bubble.movetype = MOVETYPE_NOCLIP;
  271. bubble.solid = SOLID_NOT;
  272. bubble.velocity = '0 0 15';
  273. bubble.nextthink = time + 0.5;
  274. bubble.think = bubble_bob;
  275. bubble.touch = bubble_remove;
  276. bubble.classname = "bubble";
  277. bubble.frame = 0;
  278. bubble.cnt = 0;
  279. setsize (bubble, '-8 -8 -8', '8 8 8');
  280. self.nextthink = time + random() + 0.5;
  281. self.think = make_bubbles;
  282. };
  283. void() bubble_split =
  284. {
  285. local entity bubble;
  286. bubble = spawn();
  287. setmodel (bubble, "progs/s_bubble.spr");
  288. setorigin (bubble, self.origin);
  289. bubble.movetype = MOVETYPE_NOCLIP;
  290. bubble.solid = SOLID_NOT;
  291. bubble.velocity = self.velocity;
  292. bubble.nextthink = time + 0.5;
  293. bubble.think = bubble_bob;
  294. bubble.touch = bubble_remove;
  295. bubble.classname = "bubble";
  296. bubble.frame = 1;
  297. bubble.cnt = 10;
  298. setsize (bubble, '-8 -8 -8', '8 8 8');
  299. self.frame = 1;
  300. self.cnt = 10;
  301. if (self.waterlevel != 3)
  302. remove (self);
  303. };
  304. void() bubble_remove =
  305. {
  306. if (other.classname == self.classname)
  307. {
  308. // dprint ("bump");
  309. return;
  310. }
  311. remove(self);
  312. };
  313. void() bubble_bob =
  314. {
  315. local float rnd1, rnd2, rnd3;
  316. self.cnt = self.cnt + 1;
  317. if (self.cnt == 4)
  318. bubble_split();
  319. if (self.cnt == 20)
  320. remove(self);
  321. rnd1 = self.velocity_x + (-10 + (random() * 20));
  322. rnd2 = self.velocity_y + (-10 + (random() * 20));
  323. rnd3 = self.velocity_z + 10 + random() * 10;
  324. if (rnd1 > 10)
  325. rnd1 = 5;
  326. if (rnd1 < -10)
  327. rnd1 = -5;
  328. if (rnd2 > 10)
  329. rnd2 = 5;
  330. if (rnd2 < -10)
  331. rnd2 = -5;
  332. if (rnd3 < 10)
  333. rnd3 = 15;
  334. if (rnd3 > 30)
  335. rnd3 = 25;
  336. self.velocity_x = rnd1;
  337. self.velocity_y = rnd2;
  338. self.velocity_z = rnd3;
  339. self.nextthink = time + 0.5;
  340. self.think = bubble_bob;
  341. };
  342. /*~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>
  343. ~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~*/
  344. /*QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8)
  345. Just for the debugging level. Don't use
  346. */
  347. void() viewthing =
  348. {
  349. self.movetype = MOVETYPE_NONE;
  350. self.solid = SOLID_NOT;
  351. precache_model ("progs/player.mdl");
  352. setmodel (self, "progs/player.mdl");
  353. };
  354. /*
  355. ==============================================================================
  356. SIMPLE BMODELS
  357. ==============================================================================
  358. */
  359. void() func_wall_use =
  360. { // change to alternate textures
  361. self.frame = 1 - self.frame;
  362. };
  363. /*QUAKED func_wall (0 .5 .8) ?
  364. This is just a solid wall if not inhibitted
  365. */
  366. void() func_wall =
  367. {
  368. self.angles = '0 0 0';
  369. self.movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything
  370. self.solid = SOLID_BSP;
  371. self.use = func_wall_use;
  372. setmodel (self, self.model);
  373. };
  374. /*QUAKED func_illusionary (0 .5 .8) ?
  375. A simple entity that looks solid but lets you walk through it.
  376. */
  377. void() func_illusionary =
  378. {
  379. self.angles = '0 0 0';
  380. self.movetype = MOVETYPE_NONE;
  381. self.solid = SOLID_NOT;
  382. setmodel (self, self.model);
  383. makestatic (self);
  384. };
  385. /*QUAKED func_episodegate (0 .5 .8) ? E1 E2 E3 E4
  386. This bmodel will appear if the episode has allready been completed, so players can't reenter it.
  387. */
  388. void() func_episodegate =
  389. {
  390. if (!(serverflags & self.spawnflags))
  391. return; // can still enter episode
  392. self.angles = '0 0 0';
  393. self.movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything
  394. self.solid = SOLID_BSP;
  395. self.use = func_wall_use;
  396. setmodel (self, self.model);
  397. };
  398. /*QUAKED func_bossgate (0 .5 .8) ?
  399. This bmodel appears unless players have all of the episode sigils.
  400. */
  401. const float BOSSGATE_INVERSE = 64;
  402. void() func_bossgate =
  403. {
  404. float inverse = self.spawnflags & BOSSGATE_INVERSE ? TRUE : FALSE;
  405. self.spawnflags (-) BOSSGATE_INVERSE;
  406. if(!self.spawnflags)
  407. {
  408. self.spawnflags = SIGIL_E1 | SIGIL_E2 | SIGIL_E3 | SIGIL_E4;
  409. }
  410. self.spawnflags&= SIGIL_ALL;
  411. if ( (serverflags & self.spawnflags) == self.spawnflags) //All runes collected
  412. {
  413. if(!inverse)
  414. {
  415. remove(self);
  416. return;
  417. }
  418. }
  419. else //Still missing some runes
  420. {
  421. if(inverse)
  422. {
  423. remove(self);
  424. return;
  425. }
  426. }
  427. self.angles = '0 0 0';
  428. self.movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything
  429. self.solid = SOLID_BSP;
  430. self.use = func_wall_use;
  431. setmodel (self, self.model);
  432. if(self.target || self.killtarget)
  433. {
  434. self.think = SUB_UseTargets;
  435. self.nextthink = time + 0.2;
  436. }
  437. };
  438. //============================================================================
  439. const float FUNC_HURT_START_ON = 1;
  440. void func_hurt_touch()
  441. {
  442. if(!self.state) return;
  443. if(other.health <= 0) return;
  444. if(!other.takedamage) return;
  445. if(!(other.flags & (FL_CLIENT | FL_MONSTER))) return;
  446. if(self.attack_finished > time) return;
  447. T_Damage(other, self, world, self.dmg);
  448. self.attack_finished = time + self.wait;
  449. }
  450. void func_hurt_use()
  451. {
  452. self.state = 1 - self.state;
  453. self.attack_finished = 0;
  454. }
  455. /*QUAKED func_hurt (0 .5 .8) ?
  456. Hurts alive things when they touch it
  457. */
  458. void func_hurt()
  459. {
  460. setmodel(self, self.model);
  461. setorigin(self, self.origin);
  462. self.solid = SOLID_BSP;
  463. self.movetype = MOVETYPE_PUSH;
  464. self.touch = func_hurt_touch;
  465. self.use = func_hurt_use;
  466. if(!self.dmg) self.dmg = 10;
  467. if(!self.wait) self.wait = 0.2;
  468. if(self.spawnflags & FUNC_HURT_START_ON)
  469. {
  470. self.state = 1;
  471. }
  472. }
  473. //============================================================================
  474. void trigger_changetarget_use()
  475. {
  476. SUB_SwitchTargets(target, self.target, self.killtarget);
  477. }
  478. void trigger_changetarget()
  479. {
  480. if(!self.target) objerror("Missing target.");
  481. if(!self.killtarget) objerror("Missing target to change to.");
  482. self.use = trigger_changetarget_use;
  483. }
  484. //============================================================================
  485. // Cleanup
  486. void trigger_cleanup_corpses_use()
  487. {
  488. entity e = nextent(world);
  489. while(e)
  490. {
  491. if(e.flags & FL_MONSTER)
  492. {
  493. if(e.health <= 0)
  494. {
  495. remove(e);
  496. }
  497. }
  498. e = nextent(e);
  499. }
  500. remove(self);
  501. }
  502. void trigger_cleanup_corpses()
  503. {
  504. if(coop == 0) { remove(self); return; }
  505. self.use = trigger_cleanup_corpses_use;
  506. }
  507. //============================================================================
  508. /*QUAKED ambient_suck_wind (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)
  509. */
  510. void() ambient_suck_wind =
  511. {
  512. precache_sound ("ambience/suck1.wav");
  513. ambientsound (self.origin, "ambience/suck1.wav", 1, ATTN_STATIC);
  514. makestatic(self);
  515. };
  516. /*QUAKED ambient_drone (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)
  517. */
  518. void() ambient_drone =
  519. {
  520. precache_sound ("ambience/drone6.wav");
  521. ambientsound (self.origin, "ambience/drone6.wav", 0.5, ATTN_STATIC);
  522. makestatic(self);
  523. };
  524. /*QUAKED ambient_flouro_buzz (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)
  525. */
  526. void() ambient_flouro_buzz =
  527. {
  528. precache_sound ("ambience/buzz1.wav");
  529. ambientsound (self.origin, "ambience/buzz1.wav", 1, ATTN_STATIC);
  530. makestatic(self);
  531. };
  532. /*QUAKED ambient_drip (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)
  533. */
  534. void() ambient_drip =
  535. {
  536. precache_sound ("ambience/drip1.wav");
  537. ambientsound (self.origin, "ambience/drip1.wav", 0.5, ATTN_STATIC);
  538. makestatic(self);
  539. };
  540. /*QUAKED ambient_comp_hum (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)
  541. */
  542. void() ambient_comp_hum =
  543. {
  544. precache_sound ("ambience/comp1.wav");
  545. ambientsound (self.origin, "ambience/comp1.wav", 1, ATTN_STATIC);
  546. makestatic(self);
  547. };
  548. /*QUAKED ambient_thunder (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)
  549. */
  550. void() ambient_thunder =
  551. {
  552. precache_sound ("ambience/thunder1.wav");
  553. ambientsound (self.origin, "ambience/thunder1.wav", 0.5, ATTN_STATIC);
  554. makestatic(self);
  555. };
  556. /*QUAKED ambient_light_buzz (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)
  557. */
  558. void() ambient_light_buzz =
  559. {
  560. precache_sound ("ambience/fl_hum1.wav");
  561. ambientsound (self.origin, "ambience/fl_hum1.wav", 0.5, ATTN_STATIC);
  562. makestatic(self);
  563. };
  564. /*QUAKED ambient_swamp1 (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)
  565. */
  566. void() ambient_swamp1 =
  567. {
  568. precache_sound ("ambience/swamp1.wav");
  569. ambientsound (self.origin, "ambience/swamp1.wav", 0.5, ATTN_STATIC);
  570. makestatic(self);
  571. };
  572. /*QUAKED ambient_swamp2 (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)
  573. */
  574. void() ambient_swamp2 =
  575. {
  576. precache_sound ("ambience/swamp2.wav");
  577. ambientsound (self.origin, "ambience/swamp2.wav", 0.5, ATTN_STATIC);
  578. makestatic(self);
  579. };
  580. /*QUAKED ambient_generic (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)
  581. */
  582. void() ambient_generic =
  583. {
  584. if(!self.noise)
  585. {
  586. dprint("ambient_generic with no noise. Removing.\n");
  587. remove(self);
  588. }
  589. if(!self.volume) self.volume = 0.5;
  590. if(!self.delay) self.delay = 3;
  591. precache_sound (self.noise);
  592. ambientsound (self.origin, self.noise, self.volume, self.delay);
  593. makestatic(self);
  594. };
  595. //============================================================================