g_target.c 21 KB

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