subs.qc 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  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. const float MESSAGE_ALL_PLAYERS = 2097152;
  16. void() SUB_Null = {};
  17. float(...) SUB_True = { return TRUE; };
  18. void(entity attacker, float damage) SUB_NullPain = {};
  19. void() SUB_Remove = {remove(self);};
  20. /*
  21. QuakeEd only writes a single float for angles (bad idea), so up and down are
  22. just constant angles.
  23. */
  24. void() SetMovedir =
  25. {
  26. if (self.movedir)
  27. {
  28. self.angles = '0 0 0';
  29. return;
  30. }
  31. if (self.angles == '0 -1 0')
  32. self.movedir = '0 0 1';
  33. else if (self.angles == '0 -2 0')
  34. self.movedir = '0 0 -1';
  35. else
  36. {
  37. makevectors (self.angles);
  38. self.movedir = v_forward;
  39. }
  40. self.angles = '0 0 0';
  41. };
  42. /*
  43. ================
  44. RemovedOutsideCoop
  45. Removes self if COOP_ONLY spawnflag is set and we're not in coop.
  46. Returns TRUE if it was removed.
  47. ================
  48. */
  49. float RemovedOutsideCoop()
  50. {
  51. if (!coop && (self.spawnflags & COOP_ONLY))
  52. {
  53. remove(self);
  54. return TRUE;
  55. }
  56. return FALSE;
  57. }
  58. /*
  59. ================
  60. InitTrigger
  61. ================
  62. */
  63. void() InitTrigger =
  64. {
  65. if (RemovedOutsideCoop()) return;
  66. // trigger angles are used for one-way touches. An angle of 0 is assumed
  67. // to mean no restrictions, so use a yaw of 360 instead.
  68. if (self.angles != '0 0 0')
  69. SetMovedir ();
  70. self.solid = SOLID_TRIGGER;
  71. setmodel (self, self.model); // set size and link into world
  72. self.movetype = MOVETYPE_NONE;
  73. self.modelindex = 0;
  74. self.model = "";
  75. };
  76. /*
  77. =============
  78. SUB_CalcMove
  79. calculate self.velocity and self.nextthink to reach dest from
  80. self.origin traveling at speed
  81. ===============
  82. */
  83. void(entity ent, vector tdest, float tspeed, void() func) SUB_CalcMoveEnt =
  84. {
  85. local entity stemp;
  86. stemp = self;
  87. self = ent;
  88. SUB_CalcMove (tdest, tspeed, func);
  89. self = stemp;
  90. };
  91. void(vector tdest, float tspeed, void() func) SUB_CalcMove =
  92. {
  93. local vector vdestdelta;
  94. local float len, traveltime;
  95. if (!tspeed)
  96. objerror("No speed is defined!");
  97. self.think1 = func;
  98. self.finaldest = tdest;
  99. self.think = SUB_CalcMoveDone;
  100. if (tdest == self.origin)
  101. {
  102. self.velocity = '0 0 0';
  103. self.nextthink = self.ltime + 0.1;
  104. return;
  105. }
  106. // set destdelta to the vector needed to move
  107. vdestdelta = tdest - self.origin;
  108. // calculate length of vector
  109. len = vlen (vdestdelta);
  110. // divide by speed to get time to reach dest
  111. traveltime = len / tspeed;
  112. if (traveltime < 0.1)
  113. {
  114. self.velocity = '0 0 0';
  115. self.nextthink = self.ltime + 0.1;
  116. return;
  117. }
  118. // set nextthink to trigger a think when dest is reached
  119. self.nextthink = self.ltime + traveltime;
  120. // scale the destdelta vector by the time spent traveling to get velocity
  121. self.velocity = vdestdelta * (1/traveltime); // qcc won't take vec/float
  122. };
  123. /*
  124. ============
  125. After moving, set origin to exact final destination
  126. ============
  127. */
  128. void() SUB_CalcMoveDone =
  129. {
  130. setorigin(self, self.finaldest);
  131. self.velocity = '0 0 0';
  132. self.nextthink = -1;
  133. if (self.think1)
  134. self.think1();
  135. };
  136. /*
  137. =============
  138. SUB_CalcAngleMove
  139. calculate self.avelocity and self.nextthink to reach destangle from
  140. self.angles rotating
  141. The calling function should make sure self.think is valid
  142. ===============
  143. */
  144. void(entity ent, vector destangle, float tspeed, void() func) SUB_CalcAngleMoveEnt =
  145. {
  146. local entity stemp;
  147. stemp = self;
  148. self = ent;
  149. SUB_CalcAngleMove (destangle, tspeed, func);
  150. self = stemp;
  151. };
  152. void(vector destangle, float tspeed, void() func) SUB_CalcAngleMove =
  153. {
  154. local vector destdelta;
  155. local float len, traveltime;
  156. if (!tspeed)
  157. objerror("No speed is defined!");
  158. // set destdelta to the vector needed to move
  159. destdelta = destangle - self.angles;
  160. // calculate length of vector
  161. len = vlen (destdelta);
  162. // divide by speed to get time to reach dest
  163. traveltime = len / tspeed;
  164. // set nextthink to trigger a think when dest is reached
  165. self.nextthink = self.ltime + traveltime;
  166. // scale the destdelta vector by the time spent traveling to get velocity
  167. self.avelocity = destdelta * (1 / traveltime);
  168. self.think1 = func;
  169. self.finalangle = destangle;
  170. self.think = SUB_CalcAngleMoveDone;
  171. };
  172. /*
  173. ============
  174. After rotating, set angle to exact final angle
  175. ============
  176. */
  177. void() SUB_CalcAngleMoveDone =
  178. {
  179. self.angles = self.finalangle;
  180. self.avelocity = '0 0 0';
  181. self.nextthink = -1;
  182. if (self.think1)
  183. self.think1();
  184. };
  185. //=============================================================================
  186. void() DelayThink =
  187. {
  188. activator = self.enemy;
  189. SUB_UseTargets ();
  190. remove(self);
  191. };
  192. /*
  193. ==============================
  194. SUB_UseTargets
  195. the global "activator" should be set to the entity that initiated the firing.
  196. If self.delay is set, a DelayedUse entity will be created that will actually
  197. do the SUB_UseTargets after that many seconds have passed.
  198. Centerprints any self.message to the activator.
  199. Removes all entities with a targetname that match self.killtarget,
  200. and removes them, so some events can remove other triggers.
  201. Search for (string)targetname in all entities that
  202. match (string)self.target and call their .use function
  203. ==============================
  204. */
  205. void() SUB_UseTargets =
  206. {
  207. local entity t, stemp, otemp, act;
  208. //
  209. // check for a delay
  210. //
  211. if (self.delay)
  212. {
  213. // create a temp object to fire at a later time
  214. t = spawn();
  215. t.classname = "DelayedUse";
  216. t.nextthink = time + self.delay;
  217. t.think = DelayThink;
  218. t.enemy = activator;
  219. t.message = self.message;
  220. t.killtarget = self.killtarget;
  221. t.target = self.target;
  222. t.spawnflags = self.spawnflags & MESSAGE_ALL_PLAYERS;
  223. #ifdef ALLOW_DELAYED_THINK_CANCEL
  224. t.targetname = self.targetname;
  225. t.use = SUB_Null;
  226. #endif
  227. return;
  228. }
  229. //
  230. // print the message
  231. //
  232. if (activator.classname == "player" && self.message != "")
  233. {
  234. if(self.spawnflags & MESSAGE_ALL_PLAYERS)
  235. {
  236. centerprint_all (self.message); //Ingame message, localized
  237. }
  238. else
  239. {
  240. centerprint (activator, self.message); //Ingame message, localized
  241. }
  242. if (!self.noise)
  243. sound (activator, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM);
  244. }
  245. //
  246. // kill the killtagets
  247. //
  248. if (self.killtarget)
  249. {
  250. t = find (world, targetname, self.killtarget);
  251. while( t )
  252. {
  253. remove (t);
  254. t = find (t, targetname, self.killtarget);
  255. }
  256. }
  257. //
  258. // fire targets
  259. //
  260. if (self.target)
  261. {
  262. act = activator;
  263. t = find (world, targetname, self.target);
  264. while( t )
  265. {
  266. stemp = self;
  267. otemp = other;
  268. self = t;
  269. other = stemp;
  270. if (self.use != SUB_Null)
  271. {
  272. if (self.use)
  273. self.use ();
  274. }
  275. self = stemp;
  276. other = otemp;
  277. activator = act;
  278. t = find (t, targetname, self.target);
  279. }
  280. }
  281. };
  282. /*
  283. in nightmare mode, all attack_finished times become 0
  284. some monsters refire twice automatically
  285. update: not anymore! it makes nightmare too easy
  286. */
  287. void(float normal) SUB_AttackFinished =
  288. {
  289. self.cnt = 0; // refire count for nightmare
  290. //if (skill != 3)
  291. self.attack_finished = time + normal;
  292. };
  293. float (entity targ) visible;
  294. void (void() thinkst) SUB_CheckRefire =
  295. {
  296. if (skill != 3)
  297. return;
  298. if (self.cnt == 1)
  299. return;
  300. if (!visible (self.enemy))
  301. return;
  302. self.cnt = 1;
  303. self.think = thinkst;
  304. };
  305. /*
  306. ================
  307. SUB_SwitchTargets
  308. ================
  309. */
  310. void SUB_SwitchTargets(.string field, string oldtarget, string newtarget)
  311. {
  312. entity e = find(world, targetname, oldtarget);
  313. while(e)
  314. {
  315. e.field = newtarget;
  316. e = find(e, targetname, oldtarget);
  317. }
  318. }
  319. /*
  320. ================
  321. SUB_FindWithPredicate
  322. ================
  323. */
  324. entity SUB_FindWithPredicate(entity start, .string field, string search, float(entity e) predicate = (float(entity e))SUB_True)
  325. {
  326. entity t = find(start, field, search);
  327. while(t && !predicate(t))
  328. {
  329. t = find(t, field, search);
  330. }
  331. return t;
  332. }
  333. /*
  334. ================
  335. SUB_CountTargets
  336. ================
  337. */
  338. float SUB_CountTargets(entity e, float(entity e) predicate = (float(entity e))SUB_True)
  339. {
  340. float cnt = 0;
  341. entity t = SUB_FindWithPredicate(world, targetname, e.target, predicate);
  342. while(t)
  343. {
  344. cnt++;
  345. t = SUB_FindWithPredicate(t, targetname, e.target, predicate);
  346. }
  347. return cnt;
  348. }
  349. /*
  350. ================
  351. SUB_RandomTarget
  352. ================
  353. */
  354. entity SUB_RandomTarget(entity e, float(entity e) predicate = (float(entity e))SUB_True)
  355. {
  356. float cnt = SUB_CountTargets(e, predicate);
  357. if (cnt == 0) return world;
  358. cnt = floor(cnt * random());
  359. entity t = SUB_FindWithPredicate(world, targetname, e.target, predicate);
  360. while(cnt--)
  361. {
  362. t = SUB_FindWithPredicate(t, targetname, e.target, predicate);
  363. }
  364. return t;
  365. }
  366. /*
  367. ================
  368. SUB_SetWorldType
  369. ================
  370. */
  371. void SUB_SetWorldtype()
  372. {
  373. if(self.worldtype)
  374. {
  375. self.worldtype--;
  376. }
  377. else
  378. {
  379. self.worldtype = world.worldtype;
  380. }
  381. }