g_target.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810
  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3. This program is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU General Public License
  5. as published by the Free Software Foundation; either version 2
  6. of the License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. See the GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. */
  15. #include "g_local.h"
  16. /*QUAKED target_temp_entity (1 0 0) (-8 -8 -8) (8 8 8)
  17. Fire an origin based temp entity event to the clients.
  18. "style" type byte
  19. */
  20. void Use_Target_Tent (edict_t *ent, edict_t *other, edict_t *activator)
  21. {
  22. gi.WriteByte (svc_temp_entity);
  23. gi.WriteByte (ent->style);
  24. gi.WritePosition (ent->s.origin);
  25. gi.multicast (ent->s.origin, MULTICAST_PVS);
  26. }
  27. void SP_target_temp_entity (edict_t *ent)
  28. {
  29. ent->use = Use_Target_Tent;
  30. }
  31. //==========================================================
  32. //==========================================================
  33. /*QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) looped-on looped-off reliable
  34. "noise" wav file to play
  35. "attenuation"
  36. -1 = none, send to whole level
  37. 1 = normal fighting sounds
  38. 2 = idle sound level
  39. 3 = ambient sound level
  40. "volume" 0.0 to 1.0
  41. Normal sounds play each time the target is used. The reliable flag can be set for crucial voiceovers.
  42. Looped sounds are always atten 3 / vol 1, and the use function toggles it on/off.
  43. Multiple identical looping sounds will just increase volume without any speed cost.
  44. */
  45. void Use_Target_Speaker (edict_t *ent, edict_t *other, edict_t *activator)
  46. {
  47. int chan;
  48. if (ent->spawnflags & 3)
  49. { // looping sound toggles
  50. if (ent->s.sound)
  51. ent->s.sound = 0; // turn it off
  52. else
  53. ent->s.sound = ent->noise_index; // start it
  54. }
  55. else
  56. { // normal sound
  57. if (ent->spawnflags & 4)
  58. chan = CHAN_VOICE|CHAN_RELIABLE;
  59. else
  60. chan = CHAN_VOICE;
  61. // use a positioned_sound, because this entity won't normally be
  62. // sent to any clients because it is invisible
  63. gi.positioned_sound (ent->s.origin, ent, chan, ent->noise_index, ent->volume, ent->attenuation, 0);
  64. }
  65. }
  66. void SP_target_speaker (edict_t *ent)
  67. {
  68. char buffer[MAX_QPATH];
  69. if(!st.noise)
  70. {
  71. gi.dprintf("target_speaker with no noise set at %s\n", vtos(ent->s.origin));
  72. return;
  73. }
  74. if (!strstr (st.noise, ".wav"))
  75. Com_sprintf (buffer, sizeof(buffer), "%s.wav", st.noise);
  76. else
  77. strncpy (buffer, st.noise, sizeof(buffer));
  78. ent->noise_index = gi.soundindex (buffer);
  79. if (!ent->volume)
  80. ent->volume = 1.0;
  81. if (!ent->attenuation)
  82. ent->attenuation = 1.0;
  83. else if (ent->attenuation == -1) // use -1 so 0 defaults to 1
  84. ent->attenuation = 0;
  85. // check for prestarted looping sound
  86. if (ent->spawnflags & 1)
  87. ent->s.sound = ent->noise_index;
  88. ent->use = Use_Target_Speaker;
  89. // must link the entity so we get areas and clusters so
  90. // the server can determine who to send updates to
  91. gi.linkentity (ent);
  92. }
  93. //==========================================================
  94. void Use_Target_Help (edict_t *ent, edict_t *other, edict_t *activator)
  95. {
  96. if (ent->spawnflags & 1)
  97. strncpy (game.helpmessage1, ent->message, sizeof(game.helpmessage2)-1);
  98. else
  99. strncpy (game.helpmessage2, ent->message, sizeof(game.helpmessage1)-1);
  100. game.helpchanged++;
  101. }
  102. /*QUAKED target_help (1 0 1) (-16 -16 -24) (16 16 24) help1
  103. When fired, the "message" key becomes the current personal computer string, and the message light will be set on all clients status bars.
  104. */
  105. void SP_target_help(edict_t *ent)
  106. {
  107. if (deathmatch->value)
  108. { // auto-remove for deathmatch
  109. G_FreeEdict (ent);
  110. return;
  111. }
  112. if (!ent->message)
  113. {
  114. gi.dprintf ("%s with no message at %s\n", ent->classname, vtos(ent->s.origin));
  115. G_FreeEdict (ent);
  116. return;
  117. }
  118. ent->use = Use_Target_Help;
  119. }
  120. //==========================================================
  121. /*QUAKED target_secret (1 0 1) (-8 -8 -8) (8 8 8)
  122. Counts a secret found.
  123. These are single use targets.
  124. */
  125. void use_target_secret (edict_t *ent, edict_t *other, edict_t *activator)
  126. {
  127. gi.sound (ent, CHAN_VOICE, ent->noise_index, 1, ATTN_NORM, 0);
  128. level.found_secrets++;
  129. G_UseTargets (ent, activator);
  130. G_FreeEdict (ent);
  131. }
  132. void SP_target_secret (edict_t *ent)
  133. {
  134. if (deathmatch->value)
  135. { // auto-remove for deathmatch
  136. G_FreeEdict (ent);
  137. return;
  138. }
  139. ent->use = use_target_secret;
  140. if (!st.noise)
  141. st.noise = "misc/secret.wav";
  142. ent->noise_index = gi.soundindex (st.noise);
  143. ent->svflags = SVF_NOCLIENT;
  144. level.total_secrets++;
  145. // map bug hack
  146. if (!Q_stricmp(level.mapname, "mine3") && ent->s.origin[0] == 280 && ent->s.origin[1] == -2048 && ent->s.origin[2] == -624)
  147. ent->message = "You have found a secret area.";
  148. }
  149. //==========================================================
  150. /*QUAKED target_goal (1 0 1) (-8 -8 -8) (8 8 8)
  151. Counts a goal completed.
  152. These are single use targets.
  153. */
  154. void use_target_goal (edict_t *ent, edict_t *other, edict_t *activator)
  155. {
  156. gi.sound (ent, CHAN_VOICE, ent->noise_index, 1, ATTN_NORM, 0);
  157. level.found_goals++;
  158. if (level.found_goals == level.total_goals)
  159. gi.configstring (CS_CDTRACK, "0");
  160. G_UseTargets (ent, activator);
  161. G_FreeEdict (ent);
  162. }
  163. void SP_target_goal (edict_t *ent)
  164. {
  165. if (deathmatch->value)
  166. { // auto-remove for deathmatch
  167. G_FreeEdict (ent);
  168. return;
  169. }
  170. ent->use = use_target_goal;
  171. if (!st.noise)
  172. st.noise = "misc/secret.wav";
  173. ent->noise_index = gi.soundindex (st.noise);
  174. ent->svflags = SVF_NOCLIENT;
  175. level.total_goals++;
  176. }
  177. //==========================================================
  178. /*QUAKED target_explosion (1 0 0) (-8 -8 -8) (8 8 8)
  179. Spawns an explosion temporary entity when used.
  180. "delay" wait this long before going off
  181. "dmg" how much radius damage should be done, defaults to 0
  182. */
  183. void target_explosion_explode (edict_t *self)
  184. {
  185. float save;
  186. gi.WriteByte (svc_temp_entity);
  187. gi.WriteByte (TE_EXPLOSION1);
  188. gi.WritePosition (self->s.origin);
  189. gi.multicast (self->s.origin, MULTICAST_PHS);
  190. T_RadiusDamage (self, self->activator, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE);
  191. save = self->delay;
  192. self->delay = 0;
  193. G_UseTargets (self, self->activator);
  194. self->delay = save;
  195. }
  196. void use_target_explosion (edict_t *self, edict_t *other, edict_t *activator)
  197. {
  198. self->activator = activator;
  199. if (!self->delay)
  200. {
  201. target_explosion_explode (self);
  202. return;
  203. }
  204. self->think = target_explosion_explode;
  205. self->nextthink = level.time + self->delay;
  206. }
  207. void SP_target_explosion (edict_t *ent)
  208. {
  209. ent->use = use_target_explosion;
  210. ent->svflags = SVF_NOCLIENT;
  211. }
  212. //==========================================================
  213. /*QUAKED target_changelevel (1 0 0) (-8 -8 -8) (8 8 8)
  214. Changes level to "map" when fired
  215. */
  216. void use_target_changelevel (edict_t *self, edict_t *other, edict_t *activator)
  217. {
  218. if (level.intermissiontime)
  219. return; // already activated
  220. if (!deathmatch->value && !coop->value)
  221. {
  222. if (g_edicts[1].health <= 0)
  223. return;
  224. }
  225. // if noexit, do a ton of damage to other
  226. if (deathmatch->value && !( (int)dmflags->value & DF_ALLOW_EXIT) && other != world)
  227. {
  228. T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 10 * other->max_health, 1000, 0, MOD_EXIT);
  229. return;
  230. }
  231. // if multiplayer, let everyone know who hit the exit
  232. if (deathmatch->value)
  233. {
  234. if (activator && activator->client)
  235. gi.bprintf (PRINT_HIGH, "%s exited the level.\n", activator->client->pers.netname);
  236. }
  237. // if going to a new unit, clear cross triggers
  238. if (strstr(self->map, "*"))
  239. game.serverflags &= ~(SFL_CROSS_TRIGGER_MASK);
  240. BeginIntermission (self);
  241. }
  242. void SP_target_changelevel (edict_t *ent)
  243. {
  244. if (!ent->map)
  245. {
  246. gi.dprintf("target_changelevel with no map at %s\n", vtos(ent->s.origin));
  247. G_FreeEdict (ent);
  248. return;
  249. }
  250. // ugly hack because *SOMEBODY* screwed up their map
  251. if((Q_stricmp(level.mapname, "fact1") == 0) && (Q_stricmp(ent->map, "fact3") == 0))
  252. ent->map = "fact3$secret1";
  253. ent->use = use_target_changelevel;
  254. ent->svflags = SVF_NOCLIENT;
  255. }
  256. //==========================================================
  257. /*QUAKED target_splash (1 0 0) (-8 -8 -8) (8 8 8)
  258. Creates a particle splash effect when used.
  259. Set "sounds" to one of the following:
  260. 1) sparks
  261. 2) blue water
  262. 3) brown water
  263. 4) slime
  264. 5) lava
  265. 6) blood
  266. "count" how many pixels in the splash
  267. "dmg" if set, does a radius damage at this location when it splashes
  268. useful for lava/sparks
  269. */
  270. void use_target_splash (edict_t *self, edict_t *other, edict_t *activator)
  271. {
  272. gi.WriteByte (svc_temp_entity);
  273. gi.WriteByte (TE_SPLASH);
  274. gi.WriteByte (self->count);
  275. gi.WritePosition (self->s.origin);
  276. gi.WriteDir (self->movedir);
  277. gi.WriteByte (self->sounds);
  278. gi.multicast (self->s.origin, MULTICAST_PVS);
  279. if (self->dmg)
  280. T_RadiusDamage (self, activator, self->dmg, NULL, self->dmg+40, MOD_SPLASH);
  281. }
  282. void SP_target_splash (edict_t *self)
  283. {
  284. self->use = use_target_splash;
  285. G_SetMovedir (self->s.angles, self->movedir);
  286. if (!self->count)
  287. self->count = 32;
  288. self->svflags = SVF_NOCLIENT;
  289. }
  290. //==========================================================
  291. /*QUAKED target_spawner (1 0 0) (-8 -8 -8) (8 8 8)
  292. Set target to the type of entity you want spawned.
  293. Useful for spawning monsters and gibs in the factory levels.
  294. For monsters:
  295. Set direction to the facing you want it to have.
  296. For gibs:
  297. Set direction if you want it moving and
  298. speed how fast it should be moving otherwise it
  299. will just be dropped
  300. */
  301. void ED_CallSpawn (edict_t *ent);
  302. void use_target_spawner (edict_t *self, edict_t *other, edict_t *activator)
  303. {
  304. edict_t *ent;
  305. ent = G_Spawn();
  306. ent->classname = self->target;
  307. VectorCopy (self->s.origin, ent->s.origin);
  308. VectorCopy (self->s.angles, ent->s.angles);
  309. ED_CallSpawn (ent);
  310. gi.unlinkentity (ent);
  311. KillBox (ent);
  312. gi.linkentity (ent);
  313. if (self->speed)
  314. VectorCopy (self->movedir, ent->velocity);
  315. }
  316. void SP_target_spawner (edict_t *self)
  317. {
  318. self->use = use_target_spawner;
  319. self->svflags = SVF_NOCLIENT;
  320. if (self->speed)
  321. {
  322. G_SetMovedir (self->s.angles, self->movedir);
  323. VectorScale (self->movedir, self->speed, self->movedir);
  324. }
  325. }
  326. //==========================================================
  327. /*QUAKED target_blaster (1 0 0) (-8 -8 -8) (8 8 8) NOTRAIL NOEFFECTS
  328. Fires a blaster bolt in the set direction when triggered.
  329. dmg default is 15
  330. speed default is 1000
  331. */
  332. void use_target_blaster (edict_t *self, edict_t *other, edict_t *activator)
  333. {
  334. int effect;
  335. if (self->spawnflags & 2)
  336. effect = 0;
  337. else if (self->spawnflags & 1)
  338. effect = EF_HYPERBLASTER;
  339. else
  340. effect = EF_BLASTER;
  341. fire_blaster (self, self->s.origin, self->movedir, self->dmg, self->speed, EF_BLASTER, MOD_TARGET_BLASTER);
  342. gi.sound (self, CHAN_VOICE, self->noise_index, 1, ATTN_NORM, 0);
  343. }
  344. void SP_target_blaster (edict_t *self)
  345. {
  346. self->use = use_target_blaster;
  347. G_SetMovedir (self->s.angles, self->movedir);
  348. self->noise_index = gi.soundindex ("weapons/laser2.wav");
  349. if (!self->dmg)
  350. self->dmg = 15;
  351. if (!self->speed)
  352. self->speed = 1000;
  353. self->svflags = SVF_NOCLIENT;
  354. }
  355. //==========================================================
  356. /*QUAKED target_crosslevel_trigger (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8
  357. Once this trigger is touched/used, any trigger_crosslevel_target with the same trigger number is automatically used when a level is started within the same unit. It is OK to check multiple triggers. Message, delay, target, and killtarget also work.
  358. */
  359. void trigger_crosslevel_trigger_use (edict_t *self, edict_t *other, edict_t *activator)
  360. {
  361. game.serverflags |= self->spawnflags;
  362. G_FreeEdict (self);
  363. }
  364. void SP_target_crosslevel_trigger (edict_t *self)
  365. {
  366. self->svflags = SVF_NOCLIENT;
  367. self->use = trigger_crosslevel_trigger_use;
  368. }
  369. /*QUAKED target_crosslevel_target (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8
  370. Triggered by a trigger_crosslevel elsewhere within a unit. If multiple triggers are checked, all must be true. Delay, target and
  371. killtarget also work.
  372. "delay" delay before using targets if the trigger has been activated (default 1)
  373. */
  374. void target_crosslevel_target_think (edict_t *self)
  375. {
  376. if (self->spawnflags == (game.serverflags & SFL_CROSS_TRIGGER_MASK & self->spawnflags))
  377. {
  378. G_UseTargets (self, self);
  379. G_FreeEdict (self);
  380. }
  381. }
  382. void SP_target_crosslevel_target (edict_t *self)
  383. {
  384. if (! self->delay)
  385. self->delay = 1;
  386. self->svflags = SVF_NOCLIENT;
  387. self->think = target_crosslevel_target_think;
  388. self->nextthink = level.time + self->delay;
  389. }
  390. //==========================================================
  391. /*QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON RED GREEN BLUE YELLOW ORANGE FAT
  392. When triggered, fires a laser. You can either set a target
  393. or a direction.
  394. */
  395. void target_laser_think (edict_t *self)
  396. {
  397. edict_t *ignore;
  398. vec3_t start;
  399. vec3_t end;
  400. trace_t tr;
  401. vec3_t point;
  402. vec3_t last_movedir;
  403. int count;
  404. if (self->spawnflags & 0x80000000)
  405. count = 8;
  406. else
  407. count = 4;
  408. if (self->enemy)
  409. {
  410. VectorCopy (self->movedir, last_movedir);
  411. VectorMA (self->enemy->absmin, 0.5, self->enemy->size, point);
  412. VectorSubtract (point, self->s.origin, self->movedir);
  413. VectorNormalize (self->movedir);
  414. if (!VectorCompare(self->movedir, last_movedir))
  415. self->spawnflags |= 0x80000000;
  416. }
  417. ignore = self;
  418. VectorCopy (self->s.origin, start);
  419. VectorMA (start, 2048, self->movedir, end);
  420. while(1)
  421. {
  422. tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
  423. if (!tr.ent)
  424. break;
  425. // hurt it if we can
  426. if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER))
  427. T_Damage (tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, MOD_TARGET_LASER);
  428. // if we hit something that's not a monster or player or is immune to lasers, we're done
  429. if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
  430. {
  431. if (self->spawnflags & 0x80000000)
  432. {
  433. self->spawnflags &= ~0x80000000;
  434. gi.WriteByte (svc_temp_entity);
  435. gi.WriteByte (TE_LASER_SPARKS);
  436. gi.WriteByte (count);
  437. gi.WritePosition (tr.endpos);
  438. gi.WriteDir (tr.plane.normal);
  439. gi.WriteByte (self->s.skinnum);
  440. gi.multicast (tr.endpos, MULTICAST_PVS);
  441. }
  442. break;
  443. }
  444. ignore = tr.ent;
  445. VectorCopy (tr.endpos, start);
  446. }
  447. VectorCopy (tr.endpos, self->s.old_origin);
  448. self->nextthink = level.time + FRAMETIME;
  449. }
  450. void target_laser_on (edict_t *self)
  451. {
  452. if (!self->activator)
  453. self->activator = self;
  454. self->spawnflags |= 0x80000001;
  455. self->svflags &= ~SVF_NOCLIENT;
  456. target_laser_think (self);
  457. }
  458. void target_laser_off (edict_t *self)
  459. {
  460. self->spawnflags &= ~1;
  461. self->svflags |= SVF_NOCLIENT;
  462. self->nextthink = 0;
  463. }
  464. void target_laser_use (edict_t *self, edict_t *other, edict_t *activator)
  465. {
  466. self->activator = activator;
  467. if (self->spawnflags & 1)
  468. target_laser_off (self);
  469. else
  470. target_laser_on (self);
  471. }
  472. void target_laser_start (edict_t *self)
  473. {
  474. edict_t *ent;
  475. self->movetype = MOVETYPE_NONE;
  476. self->solid = SOLID_NOT;
  477. self->s.renderfx |= RF_BEAM|RF_TRANSLUCENT;
  478. self->s.modelindex = 1; // must be non-zero
  479. // set the beam diameter
  480. if (self->spawnflags & 64)
  481. self->s.frame = 16;
  482. else
  483. self->s.frame = 4;
  484. // set the color
  485. if (self->spawnflags & 2)
  486. self->s.skinnum = 0xf2f2f0f0;
  487. else if (self->spawnflags & 4)
  488. self->s.skinnum = 0xd0d1d2d3;
  489. else if (self->spawnflags & 8)
  490. self->s.skinnum = 0xf3f3f1f1;
  491. else if (self->spawnflags & 16)
  492. self->s.skinnum = 0xdcdddedf;
  493. else if (self->spawnflags & 32)
  494. self->s.skinnum = 0xe0e1e2e3;
  495. if (!self->enemy)
  496. {
  497. if (self->target)
  498. {
  499. ent = G_Find (NULL, FOFS(targetname), self->target);
  500. if (!ent)
  501. gi.dprintf ("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target);
  502. self->enemy = ent;
  503. }
  504. else
  505. {
  506. G_SetMovedir (self->s.angles, self->movedir);
  507. }
  508. }
  509. self->use = target_laser_use;
  510. self->think = target_laser_think;
  511. if (!self->dmg)
  512. self->dmg = 1;
  513. VectorSet (self->mins, -8, -8, -8);
  514. VectorSet (self->maxs, 8, 8, 8);
  515. gi.linkentity (self);
  516. if (self->spawnflags & 1)
  517. target_laser_on (self);
  518. else
  519. target_laser_off (self);
  520. }
  521. void SP_target_laser (edict_t *self)
  522. {
  523. // let everything else get spawned before we start firing
  524. self->think = target_laser_start;
  525. self->nextthink = level.time + 1;
  526. }
  527. //==========================================================
  528. /*QUAKED target_lightramp (0 .5 .8) (-8 -8 -8) (8 8 8) TOGGLE
  529. speed How many seconds the ramping will take
  530. message two letters; starting lightlevel and ending lightlevel
  531. */
  532. void target_lightramp_think (edict_t *self)
  533. {
  534. char style[2];
  535. style[0] = 'a' + self->movedir[0] + (level.time - self->timestamp) / FRAMETIME * self->movedir[2];
  536. style[1] = 0;
  537. gi.configstring (CS_LIGHTS+self->enemy->style, style);
  538. if ((level.time - self->timestamp) < self->speed)
  539. {
  540. self->nextthink = level.time + FRAMETIME;
  541. }
  542. else if (self->spawnflags & 1)
  543. {
  544. char temp;
  545. temp = self->movedir[0];
  546. self->movedir[0] = self->movedir[1];
  547. self->movedir[1] = temp;
  548. self->movedir[2] *= -1;
  549. }
  550. }
  551. void target_lightramp_use (edict_t *self, edict_t *other, edict_t *activator)
  552. {
  553. if (!self->enemy)
  554. {
  555. edict_t *e;
  556. // check all the targets
  557. e = NULL;
  558. while (1)
  559. {
  560. e = G_Find (e, FOFS(targetname), self->target);
  561. if (!e)
  562. break;
  563. if (strcmp(e->classname, "light") != 0)
  564. {
  565. gi.dprintf("%s at %s ", self->classname, vtos(self->s.origin));
  566. gi.dprintf("target %s (%s at %s) is not a light\n", self->target, e->classname, vtos(e->s.origin));
  567. }
  568. else
  569. {
  570. self->enemy = e;
  571. }
  572. }
  573. if (!self->enemy)
  574. {
  575. gi.dprintf("%s target %s not found at %s\n", self->classname, self->target, vtos(self->s.origin));
  576. G_FreeEdict (self);
  577. return;
  578. }
  579. }
  580. self->timestamp = level.time;
  581. target_lightramp_think (self);
  582. }
  583. void SP_target_lightramp (edict_t *self)
  584. {
  585. if (!self->message || strlen(self->message) != 2 || self->message[0] < 'a' || self->message[0] > 'z' || self->message[1] < 'a' || self->message[1] > 'z' || self->message[0] == self->message[1])
  586. {
  587. gi.dprintf("target_lightramp has bad ramp (%s) at %s\n", self->message, vtos(self->s.origin));
  588. G_FreeEdict (self);
  589. return;
  590. }
  591. if (deathmatch->value)
  592. {
  593. G_FreeEdict (self);
  594. return;
  595. }
  596. if (!self->target)
  597. {
  598. gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
  599. G_FreeEdict (self);
  600. return;
  601. }
  602. self->svflags |= SVF_NOCLIENT;
  603. self->use = target_lightramp_use;
  604. self->think = target_lightramp_think;
  605. self->movedir[0] = self->message[0] - 'a';
  606. self->movedir[1] = self->message[1] - 'a';
  607. self->movedir[2] = (self->movedir[1] - self->movedir[0]) / (self->speed / FRAMETIME);
  608. }
  609. //==========================================================
  610. /*QUAKED target_earthquake (1 0 0) (-8 -8 -8) (8 8 8)
  611. When triggered, this initiates a level-wide earthquake.
  612. All players and monsters are affected.
  613. "speed" severity of the quake (default:200)
  614. "count" duration of the quake (default:5)
  615. */
  616. void target_earthquake_think (edict_t *self)
  617. {
  618. int i;
  619. edict_t *e;
  620. if (self->last_move_time < level.time)
  621. {
  622. gi.positioned_sound (self->s.origin, self, CHAN_AUTO, self->noise_index, 1.0, ATTN_NONE, 0);
  623. self->last_move_time = level.time + 0.5;
  624. }
  625. for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++)
  626. {
  627. if (!e->inuse)
  628. continue;
  629. if (!e->client)
  630. continue;
  631. if (!e->groundentity)
  632. continue;
  633. e->groundentity = NULL;
  634. e->velocity[0] += crandom()* 150;
  635. e->velocity[1] += crandom()* 150;
  636. e->velocity[2] = self->speed * (100.0 / e->mass);
  637. }
  638. if (level.time < self->timestamp)
  639. self->nextthink = level.time + FRAMETIME;
  640. }
  641. void target_earthquake_use (edict_t *self, edict_t *other, edict_t *activator)
  642. {
  643. self->timestamp = level.time + self->count;
  644. self->nextthink = level.time + FRAMETIME;
  645. self->activator = activator;
  646. self->last_move_time = 0;
  647. }
  648. void SP_target_earthquake (edict_t *self)
  649. {
  650. if (!self->targetname)
  651. gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
  652. if (!self->count)
  653. self->count = 5;
  654. if (!self->speed)
  655. self->speed = 200;
  656. self->svflags |= SVF_NOCLIENT;
  657. self->think = target_earthquake_think;
  658. self->use = target_earthquake_use;
  659. self->noise_index = gi.soundindex ("world/quake.wav");
  660. }