triggers.qc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. entity stemp, otemp, s, old;
  2. void() trigger_reactivate =
  3. {
  4. self.solid = SOLID_TRIGGER;
  5. };
  6. float USED_SPAWN_PARMS = 8;
  7. void() EncodeLevelParms =
  8. {
  9. other.items = other.items - (other.items & (IT_KEY1 | IT_KEY2) );
  10. if (other.health > 100)
  11. other.health = 100;
  12. parm1 = other.items;
  13. parm2 = other.health;
  14. parm3 = other.armorvalue;
  15. parm4 = other.ammo_shells;
  16. parm5 = other.ammo_nails;
  17. parm6 = other.ammo_rockets;
  18. parm7 = other.weapon;
  19. parm8 = other.armortype;
  20. };
  21. void() SetNewGameParms =
  22. {
  23. other = self;
  24. other.health = 100;
  25. other.ammo_shells = 25;
  26. other.ammo_nails = 0;
  27. other.ammo_rockets = 0;
  28. other.ammo_cells = 0;
  29. other.items = IT_SHOTGUN | IT_AXE;
  30. other.weapon = 1;
  31. other.armortype = 0;
  32. other.armorvalue = 0;
  33. EncodeLevelParms ();
  34. };
  35. void() DecodeLevelParms =
  36. {
  37. self.items = parm1;
  38. self.health = parm2;
  39. self.armorvalue = parm3;
  40. self.ammo_shells = parm4;
  41. self.ammo_nails = parm5;
  42. self.ammo_rockets = parm6;
  43. self.weapon = parm7;
  44. self.armortype = parm8;
  45. };
  46. void() T_changelevel =
  47. {
  48. if (other.classname != "player")
  49. return;
  50. self.nextthink = time + 10;
  51. self.think = trigger_reactivate;
  52. self.solid = SOLID_NOT;
  53. EncodeLevelParms ();
  54. bprint ("\n\n");
  55. bprint (other.netname);
  56. bprint (" killed ");
  57. bprint (ftos(other.killed_monsters));
  58. bprint (" monsters out of ");
  59. bprint (ftos(total_monsters));
  60. bprint ("\n");
  61. bprint ("And found ");
  62. bprint (ftos(other.found_secrets));
  63. bprint (" secrets out of ");
  64. bprint (ftos(total_secrets));
  65. bprint ("\n\n");
  66. changelevel (other, self.map, USED_SPAWN_PARMS);
  67. };
  68. /*QUAKED trigger_changelevel (0.5 0.5 0.5) ?
  69. When the player touches this, he gets sent to the map listed in the "map" variable.
  70. */
  71. void() trigger_changelevel =
  72. {
  73. if (!self.map)
  74. objerror ("chagnelevel trigger doesn't have map");
  75. self.angles = '0 0 0';
  76. self.solid = SOLID_TRIGGER;
  77. settriggermodel (self, self.model);
  78. self.touch = T_changelevel;
  79. };
  80. //=============================================================================
  81. float SPAWNFLAG_NOMESSAGE = 1;
  82. float SPAWNFLAG_NOTOUCH = 1;
  83. // the wait time has passed, so set back up for another activation
  84. void() multi_wait =
  85. {
  86. if (self.max_health)
  87. {
  88. self.health = self.max_health;
  89. self.takedamage = DAMAGE_YES;
  90. self.solid = SOLID_BBOX;
  91. }
  92. else
  93. self.solid = SOLID_TRIGGER;
  94. };
  95. // the delay time has passed, so activate all targets
  96. void() multi_fire =
  97. {
  98. activator = self.enemy;
  99. SUB_UseTargets();
  100. if (self.wait > 0)
  101. {
  102. self.think = multi_wait;
  103. self.nextthink = time + self.wait;
  104. }
  105. else
  106. self.nextthink = -1;
  107. };
  108. // the trigger was just touched/killed/used
  109. // self.enemy should be set to the activator so it can be held through a delay
  110. // so wait for the delay time before firing
  111. void() multi_trigger =
  112. {
  113. if (self.nextthink > time)
  114. {
  115. return; // allready been triggered
  116. }
  117. if (self.classname == "trigger_secret")
  118. {
  119. if (self.enemy.classname != "player")
  120. return;
  121. self.enemy.found_secrets = self.enemy.found_secrets + 1;
  122. WriteByte (self.enemy, SVC_FOUNDSECRET);
  123. }
  124. if (self.message)
  125. {
  126. if (self.enemy.classname == "player")
  127. {
  128. sound (self.enemy, 0, "temp/talk.wav", 1,1);
  129. centerprint (self.enemy, self.message);
  130. }
  131. }
  132. if (self.noise)
  133. sound (self, 1, self.noise, 1, 1);
  134. // don't trigger again until reset
  135. self.solid = SOLID_NOT;
  136. self.takedamage = DAMAGE_NO;
  137. // either fire now, or after a delay
  138. if (!self.delay)
  139. {
  140. multi_fire ();
  141. if (self.wait == -1)
  142. remove(self);
  143. }
  144. else
  145. {
  146. self.nextthink = time + self.delay;
  147. self.think = multi_fire;
  148. }
  149. };
  150. void() multi_killed =
  151. {
  152. self.enemy = damage_attacker;
  153. multi_trigger();
  154. };
  155. void() multi_use =
  156. {
  157. self.enemy = activator;
  158. multi_trigger();
  159. };
  160. void() multi_touch =
  161. {
  162. local vector for;
  163. if (other.classname != "player")
  164. return;
  165. // if the trigger has an angles field, check player's facing direction
  166. if (self.angles != '0 0 0')
  167. {
  168. makevectors (self.angles);
  169. for = v_forward;
  170. makevectors (other.angles);
  171. if (v_forward * for < 0)
  172. return; // not facing the right way
  173. }
  174. self.enemy = other;
  175. multi_trigger ();
  176. };
  177. /*QUAKED trigger_multiple (.5 .5 .5) ? notouch
  178. Variable sized repeatable trigger. Must be targeted at one or more entities. If "health" is set, the trigger must be killed to activate each time.
  179. If "delay" is set, the trigger waits some time after activating before firing.
  180. "wait" : Seconds between triggerings. (.2 default)
  181. If notouch is set, the trigger is only fired by other entities, not by touching.
  182. sounds
  183. 1) secret
  184. 2) beep beep
  185. 3) large switch
  186. 4)
  187. set "message" to text string
  188. */
  189. void() trigger_multiple =
  190. {
  191. if (self.sounds == 1)
  192. {
  193. precache_sound ("temp/secret.wav");
  194. self.noise = "temp/secret.wav";
  195. }
  196. else if (self.sounds == 2)
  197. {
  198. precache_sound ("temp/talk.wav");
  199. self.noise = "temp/talk.wav";
  200. }
  201. else if (self.sounds == 3)
  202. {
  203. precache_sound ("misc/trigger1.wav");
  204. self.noise = "misc/trigger1.wav";
  205. }
  206. // self.angles = '0 0 0';
  207. if (!self.wait)
  208. self.wait = 0.2;
  209. self.use = multi_use;
  210. if (self.health)
  211. {
  212. if (self.spawnflags & SPAWNFLAG_NOTOUCH)
  213. objerror ("health and notouch don't make sense\n");
  214. self.max_health = self.health;
  215. self.th_die = multi_killed;
  216. self.takedamage = DAMAGE_YES;
  217. self.solid = SOLID_BBOX;
  218. }
  219. else
  220. {
  221. if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
  222. {
  223. self.touch = multi_touch;
  224. self.solid = SOLID_TRIGGER;
  225. }
  226. }
  227. settriggermodel (self, self.model);
  228. if (!self.target)
  229. {
  230. if (!self.message)
  231. error ("There is no target set!");
  232. }
  233. };
  234. /*QUAKED trigger_once (.5 .5 .5) ? notouch
  235. Variable sized trigger. Triggers once, then removes itself. You must set the key "target" to the name of another object in the level that has a matching
  236. "targetname". If "health" is set, the trigger must be killed to activate.
  237. If notouch is set, the trigger is only fired by other entities, not by touching.
  238. if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
  239. if "angle" is set, the trigger will only fire when someone is facing the direction of the angle. Use "360" for an angle of 0.
  240. sounds
  241. 1) secret
  242. 2) beep beep
  243. 3) large switch
  244. 4)
  245. set "message" to text string
  246. */
  247. void() trigger_once =
  248. {
  249. self.wait = -1;
  250. trigger_multiple();
  251. };
  252. /*QUAKED trigger_secret (.5 .5 .5) ?
  253. secret counter trigger
  254. sounds
  255. 1) secret
  256. 2) beep beep
  257. 3)
  258. 4)
  259. set "message" to text string
  260. */
  261. void() trigger_secret =
  262. {
  263. total_secrets = total_secrets + 1;
  264. self.wait = -1;
  265. // self.classname = "trigger_secret";
  266. if (!self.message)
  267. self.message = "You found a secret area!";
  268. if (!self.sounds)
  269. self.sounds = 1;
  270. if (self.sounds == 1)
  271. {
  272. precache_sound ("temp/secret.wav");
  273. self.noise = "temp/secret.wav";
  274. }
  275. else if (self.sounds == 2)
  276. {
  277. precache_sound ("temp/talk.wav");
  278. self.noise = "temp/talk.wav";
  279. }
  280. trigger_multiple ();
  281. };
  282. void() counter_use =
  283. {
  284. local string junk;
  285. self.count = self.count - 1;
  286. if (self.count < 0)
  287. return;
  288. if (self.count != 0)
  289. {
  290. if (activator.classname == "player"
  291. && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
  292. {
  293. if (self.count > 4)
  294. centerprint (activator, "There are mroe to go...");
  295. else if (self.count == 3)
  296. centerprint (activator, "Only 3 more to go...");
  297. else if (self.count == 2)
  298. centerprint (activator, "Only 2 more to go...");
  299. else
  300. centerprint (activator, "Only 1 more to go...");
  301. }
  302. return;
  303. }
  304. if (activator.classname == "player"
  305. && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
  306. centerprint(activator, "Sequence completed!");
  307. self.enemy = activator;
  308. multi_trigger ();
  309. };
  310. /*QUAKED trigger_counter (.5 .5 .5) ? nomessage
  311. Acts as an intermediary for an action that takes multiple inputs.
  312. If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
  313. After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
  314. */
  315. void() trigger_counter =
  316. {
  317. self.wait = -1;
  318. if (!self.count)
  319. self.count = 2;
  320. if (!self.target)
  321. error ("There is no target set!");
  322. self.use = counter_use;
  323. };
  324. /*
  325. ==============================================================================
  326. TELEPORT TRIGGERS
  327. ==============================================================================
  328. */
  329. float PLAYER_ONLY = 1;
  330. void() random_telesound =
  331. {
  332. local float v;
  333. local string tmpstr;
  334. v = random() * 5;
  335. if (v < 1)
  336. tmpstr = "misc/r_tele1.wav";
  337. else if (v < 2)
  338. tmpstr = "misc/r_tele2.wav";
  339. else if (v < 3)
  340. tmpstr = "misc/r_tele3.wav";
  341. else if (v < 4)
  342. tmpstr = "misc/r_tele4.wav";
  343. else
  344. tmpstr = "misc/r_tele5.wav";
  345. sound (self, 1, tmpstr, 1, 1);
  346. };
  347. void() tfog1 = [ 0, tfog2 ] {};
  348. void() tfog2 = [ 1, tfog3 ] {random_telesound();};
  349. void() tfog3 = [ 2, tfog4 ] {};
  350. void() tfog4 = [ 3, tfog5 ] {};
  351. void() tfog5 = [ 4, tfog6 ] {};
  352. void() tfog6 = [ 5, tfog7 ] {};
  353. void() tfog7 = [ 6, tfog8 ] {};
  354. void() tfog8 = [ 7, tfog9 ] {};
  355. void() tfog9 = [ 8, tfog10 ] {};
  356. void() tfog10 = [ 9, tfog11 ] {};
  357. void() tfog11 = [ 9, tfog11 ] {remove(self);};
  358. void(vector org) spawn_tfog =
  359. {
  360. s = spawn ();
  361. s.origin = org;
  362. s.angles = '0 0 0';
  363. s.movetype = MOVETYPE_NONE;
  364. s.solid = SOLID_NOT;
  365. setmodel (s, "sprites/s_telep.spr");
  366. old = self;
  367. self = s;
  368. tfog1 ();
  369. self = old;
  370. };
  371. void() tdeath_touch =
  372. {
  373. if (other == self.owner)
  374. return;
  375. if (other.health)
  376. {
  377. self.solid = SOLID_NOT;
  378. T_Damage (other, self, self, 1000);
  379. }
  380. };
  381. void() tdeath_remove =
  382. {
  383. remove (self);
  384. return;
  385. };
  386. void(vector org, entity death_owner) spawn_tdeath =
  387. {
  388. local entity death;
  389. death = spawn();
  390. death.classname = "teledeath";
  391. death.origin = org;
  392. death.movetype = MOVETYPE_NONE;
  393. death.solid = SOLID_TRIGGER;
  394. death.angles = '0 0 0';
  395. // FIX ME (this does not set the size properly)
  396. setsize (death, '-20 -20 -20', '20 20 20');
  397. death.touch = tdeath_touch;
  398. death.nextthink = time + 0.1;
  399. death.think = tdeath_remove;
  400. death.owner = death_owner;
  401. };
  402. void() teleport_touch =
  403. {
  404. local entity t;
  405. local vector org;
  406. if (self.spawnflags & PLAYER_ONLY)
  407. {
  408. if (other.classname != "player")
  409. return;
  410. }
  411. // only teleport living creatures
  412. if (other.health <= 0)
  413. return;
  414. // put a tfog where the player was
  415. spawn_tfog (other.origin);
  416. // FIXME: precalc at awake time
  417. t = find (world, targetname, self.target);
  418. if (!t)
  419. objerror ("couldn't find target");
  420. // spawn a tfog flash in front of the destination
  421. makevectors (t.mangle);
  422. org = t.origin + 32 * v_forward;
  423. spawn_tfog (org);
  424. spawn_tdeath(t.origin, other);
  425. // move the player and lock him down for a little while
  426. if (!other.health)
  427. {
  428. other.origin = t.origin;
  429. other.velocity = (v_forward * other.velocity_x) + (v_forward * other.velocity_y);
  430. return;
  431. }
  432. setorigin (other, t.origin);
  433. other.angles = t.mangle;
  434. if (other.classname == "player")
  435. {
  436. other.fixangle = 1; // turn this way immediately
  437. other.teleport_time = time + 0.7;
  438. if (other.flags & FL_ONGROUND)
  439. other.flags = other.flags - FL_ONGROUND;
  440. other.velocity = v_forward * 300;
  441. }
  442. other.flags = other.flags - other.flags & FL_ONGROUND;
  443. };
  444. /*QUAKED info_teleport_destination (.5 .5 .5) (-8 -8 -8) (8 8 32)
  445. This is the destination marker for a teleporter. It should have a "targetname" field with the same value as a teleporter's "target" field.
  446. */
  447. void() info_teleport_destination =
  448. {
  449. // this does nothing, just serves as a target spot
  450. self.mangle = self.angles;
  451. self.angles = '0 0 0';
  452. self.model = "";
  453. self.origin = self.origin + '0 0 27';
  454. if (!self.targetname)
  455. objerror ("no targetname");
  456. };
  457. /*QUAKED trigger_teleport (.5 .5 .5) ? PLAYER_ONLY
  458. Any object touching this will be transported to the corresponding info_teleport_destination entity. You must set the "target" field, and create an object with a "targetname" field that matches.
  459. */
  460. void() trigger_teleport =
  461. {
  462. self.mangle = self.angles;
  463. self.angles = '0 0 0';
  464. setsize (self, self.mins, self.maxs);
  465. self.solid = SOLID_TRIGGER;
  466. settriggermodel (self, self.model);
  467. self.touch = teleport_touch;
  468. self.angles = '0 0 0';
  469. // find the destination
  470. if (!self.target)
  471. objerror ("no target");
  472. };
  473. /*
  474. ==============================================================================
  475. trigger_setskill
  476. ==============================================================================
  477. */
  478. void() trigger_skill_touch =
  479. {
  480. if (other.classname != "player")
  481. return;
  482. cvar_set ("skill", self.message);
  483. };
  484. /*QUAKED trigger_setskill (.5 .5 .5) ?
  485. sets skill level to the value of "message".
  486. Only used on start map.
  487. */
  488. void() trigger_setskill =
  489. {
  490. self.mangle = self.angles;
  491. self.angles = '0 0 0';
  492. setsize (self, self.mins, self.maxs);
  493. self.solid = SOLID_TRIGGER;
  494. settriggermodel (self, self.model);
  495. self.touch = trigger_skill_touch;
  496. self.angles = '0 0 0';
  497. };
  498. /*
  499. ==============================================================================
  500. ONLY REGISTERED TRIGGERS
  501. ==============================================================================
  502. */
  503. void() trigger_onlyregistered_touch =
  504. {
  505. if (self.attack_finished > time)
  506. return;
  507. self.attack_finished = time + 2;
  508. if (cvar("registered"))
  509. {
  510. SUB_UseTargets ();
  511. remove (self);
  512. }
  513. else
  514. {
  515. centerprint (other, self.message);
  516. sound (other, 0, "temp/talk.wav", 1,1);
  517. }
  518. };
  519. /*QUAKED trigger_onlyregistered (.5 .5 .5) ?
  520. Only fires if playing the registered version, otherwise prints the message
  521. */
  522. void() trigger_onlyregistered =
  523. {
  524. self.mangle = self.angles;
  525. self.angles = '0 0 0';
  526. setsize (self, self.mins, self.maxs);
  527. precache_sound ("temp/talk.wav");
  528. self.solid = SOLID_TRIGGER;
  529. settriggermodel (self, self.model);
  530. self.touch = trigger_onlyregistered_touch;
  531. self.angles = '0 0 0';
  532. };
  533. //============================================================================
  534. void() hurt_on =
  535. {
  536. self.solid = SOLID_TRIGGER;
  537. self.nextthink = -1;
  538. };
  539. void() hurt_touch =
  540. {
  541. if (other.health)
  542. {
  543. self.solid = SOLID_NOT;
  544. T_Damage (other, self, self, self.dmg);
  545. self.think = hurt_on;
  546. self.nextthink = time + 1;
  547. }
  548. return;
  549. };
  550. /*QUAKED trigger_hurt (.5 .5 .5) ?
  551. Any object touching this will be hurt
  552. set dmg to damage amount
  553. defalt dmg = 5
  554. */
  555. void() trigger_hurt =
  556. {
  557. self.mangle = self.angles;
  558. self.angles = '0 0 0';
  559. self.solid = SOLID_TRIGGER;
  560. settriggermodel (self, self.model);
  561. self.touch = hurt_touch;
  562. self.angles = '0 0 0';
  563. if (!self.dmg)
  564. self.dmg = 5;
  565. };
  566. /*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE
  567. Pushes the player
  568. */
  569. float PUSH_ONCE = 1;
  570. void() trigger_push_touch;
  571. void() trigger_push =
  572. {
  573. self.movetype = MOVETYPE_NONE;
  574. self.solid = SOLID_TRIGGER;
  575. settriggermodel (self, self.model);
  576. self.touch = trigger_push_touch;
  577. SetMovedir();
  578. };
  579. void() trigger_push_touch =
  580. {
  581. if (other.classname == "player")
  582. other.velocity = 1000 * self.movedir + 400 * '0 0 1';
  583. if (self.spawnflags & PUSH_ONCE)
  584. remove(self);
  585. };
  586. //============================================================================
  587. void() trigger_monsterjump_touch =
  588. {
  589. if ( other.flags & (FL_MONSTER | FL_FLY | FL_SWIM) != FL_MONSTER )
  590. return;
  591. // set XY even if not on ground, so the jump will clear lips
  592. other.velocity_x = self.movedir_x * self.speed;
  593. other.velocity_y = self.movedir_y * self.speed;
  594. if ( !(other.flags & FL_ONGROUND) )
  595. return;
  596. other.flags = other.flags - FL_ONGROUND;
  597. other.velocity_z = self.height;
  598. };
  599. /*QUAKED trigger_monsterjump (.5 .5 .5) ?
  600. Walking monsters that touch this will jump in the direction of the trigger's angle
  601. "speed" default to 200, the speed thrown forward
  602. "height" default to 200, the speed thrown upwards
  603. */
  604. void() trigger_monsterjump =
  605. {
  606. if (!self.speed)
  607. self.speed = 200;
  608. if (!self.height)
  609. self.height = 200;
  610. self.movetype = MOVETYPE_NONE;
  611. self.solid = SOLID_TRIGGER;
  612. settriggermodel (self, self.model);
  613. self.touch = trigger_monsterjump_touch;
  614. SetMovedir();
  615. };