g_target.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  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 (!Q_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((Q_stricmp(level.mapname, "fact1") == 0) && (Q_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)
  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. VectorCopy (self->s.origin, ent->s.origin);
  296. VectorCopy (self->s.angles, ent->s.angles);
  297. ED_CallSpawn (ent);
  298. gi.unlinkentity (ent);
  299. KillBox (ent);
  300. gi.linkentity (ent);
  301. if (self->speed)
  302. VectorCopy (self->movedir, ent->velocity);
  303. ent->s.renderfx |= RF_IR_VISIBLE; //PGM
  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 WINDOWSTOP
  381. When triggered, fires a laser. You can either set a target
  382. or a direction.
  383. WINDOWSTOP - stops at CONTENTS_WINDOW
  384. */
  385. //======
  386. // PGM
  387. #define LASER_ON 0x0001
  388. #define LASER_RED 0x0002
  389. #define LASER_GREEN 0x0004
  390. #define LASER_BLUE 0x0008
  391. #define LASER_YELLOW 0x0010
  392. #define LASER_ORANGE 0x0020
  393. #define LASER_FAT 0x0040
  394. #define LASER_STOPWINDOW 0x0080
  395. // PGM
  396. //======
  397. void target_laser_think (edict_t *self)
  398. {
  399. edict_t *ignore;
  400. vec3_t start;
  401. vec3_t end;
  402. trace_t tr;
  403. vec3_t point;
  404. vec3_t last_movedir;
  405. int count;
  406. if (self->spawnflags & 0x80000000)
  407. count = 8;
  408. else
  409. count = 4;
  410. if (self->enemy)
  411. {
  412. VectorCopy (self->movedir, last_movedir);
  413. VectorMA (self->enemy->absmin, 0.5, self->enemy->size, point);
  414. VectorSubtract (point, self->s.origin, self->movedir);
  415. VectorNormalize (self->movedir);
  416. if (!VectorCompare(self->movedir, last_movedir))
  417. self->spawnflags |= 0x80000000;
  418. }
  419. ignore = self;
  420. VectorCopy (self->s.origin, start);
  421. VectorMA (start, 2048, self->movedir, end);
  422. while(1)
  423. {
  424. //======
  425. // PGM
  426. if(self->spawnflags & LASER_STOPWINDOW)
  427. tr = gi.trace (start, NULL, NULL, end, ignore, MASK_SHOT);
  428. else
  429. tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
  430. // PGM
  431. //======
  432. if (!tr.ent)
  433. break;
  434. // hurt it if we can
  435. if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER))
  436. T_Damage (tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, MOD_TARGET_LASER);
  437. // if we hit something that's not a monster or player or is immune to lasers, we're done
  438. // if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
  439. //PMM added SVF_DAMAGEABLE
  440. if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client) && !(tr.ent->svflags & SVF_DAMAGEABLE))
  441. {
  442. if (self->spawnflags & 0x80000000)
  443. {
  444. self->spawnflags &= ~0x80000000;
  445. gi.WriteByte (svc_temp_entity);
  446. gi.WriteByte (TE_LASER_SPARKS);
  447. gi.WriteByte (count);
  448. gi.WritePosition (tr.endpos);
  449. gi.WriteDir (tr.plane.normal);
  450. gi.WriteByte (self->s.skinnum);
  451. gi.multicast (tr.endpos, MULTICAST_PVS);
  452. }
  453. break;
  454. }
  455. ignore = tr.ent;
  456. VectorCopy (tr.endpos, start);
  457. }
  458. VectorCopy (tr.endpos, self->s.old_origin);
  459. self->nextthink = level.time + FRAMETIME;
  460. }
  461. void target_laser_on (edict_t *self)
  462. {
  463. if (!self->activator)
  464. self->activator = self;
  465. self->spawnflags |= 0x80000001;
  466. self->svflags &= ~SVF_NOCLIENT;
  467. target_laser_think (self);
  468. }
  469. void target_laser_off (edict_t *self)
  470. {
  471. self->spawnflags &= ~1;
  472. self->svflags |= SVF_NOCLIENT;
  473. self->nextthink = 0;
  474. }
  475. void target_laser_use (edict_t *self, edict_t *other, edict_t *activator)
  476. {
  477. self->activator = activator;
  478. if (self->spawnflags & 1)
  479. target_laser_off (self);
  480. else
  481. target_laser_on (self);
  482. }
  483. void target_laser_start (edict_t *self)
  484. {
  485. edict_t *ent;
  486. self->movetype = MOVETYPE_NONE;
  487. self->solid = SOLID_NOT;
  488. self->s.renderfx |= RF_BEAM|RF_TRANSLUCENT;
  489. self->s.modelindex = 1; // must be non-zero
  490. // set the beam diameter
  491. if (self->spawnflags & 64)
  492. self->s.frame = 16;
  493. else
  494. self->s.frame = 4;
  495. // set the color
  496. if (self->spawnflags & 2)
  497. self->s.skinnum = 0xf2f2f0f0;
  498. else if (self->spawnflags & 4)
  499. self->s.skinnum = 0xd0d1d2d3;
  500. else if (self->spawnflags & 8)
  501. self->s.skinnum = 0xf3f3f1f1;
  502. else if (self->spawnflags & 16)
  503. self->s.skinnum = 0xdcdddedf;
  504. else if (self->spawnflags & 32)
  505. self->s.skinnum = 0xe0e1e2e3;
  506. if (!self->enemy)
  507. {
  508. if (self->target)
  509. {
  510. ent = G_Find (NULL, FOFS(targetname), self->target);
  511. if (!ent)
  512. gi.dprintf ("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target);
  513. self->enemy = ent;
  514. }
  515. else
  516. {
  517. G_SetMovedir (self->s.angles, self->movedir);
  518. }
  519. }
  520. self->use = target_laser_use;
  521. self->think = target_laser_think;
  522. if (!self->dmg)
  523. self->dmg = 1;
  524. VectorSet (self->mins, -8, -8, -8);
  525. VectorSet (self->maxs, 8, 8, 8);
  526. gi.linkentity (self);
  527. if (self->spawnflags & 1)
  528. target_laser_on (self);
  529. else
  530. target_laser_off (self);
  531. }
  532. void SP_target_laser (edict_t *self)
  533. {
  534. // let everything else get spawned before we start firing
  535. self->think = target_laser_start;
  536. self->nextthink = level.time + 1;
  537. }
  538. //==========================================================
  539. /*QUAKED target_lightramp (0 .5 .8) (-8 -8 -8) (8 8 8) TOGGLE
  540. speed How many seconds the ramping will take
  541. message two letters; starting lightlevel and ending lightlevel
  542. */
  543. void target_lightramp_think (edict_t *self)
  544. {
  545. char style[2];
  546. style[0] = 'a' + self->movedir[0] + (level.time - self->timestamp) / FRAMETIME * self->movedir[2];
  547. style[1] = 0;
  548. gi.configstring (CS_LIGHTS+self->enemy->style, style);
  549. if ((level.time - self->timestamp) < self->speed)
  550. {
  551. self->nextthink = level.time + FRAMETIME;
  552. }
  553. else if (self->spawnflags & 1)
  554. {
  555. char temp;
  556. temp = self->movedir[0];
  557. self->movedir[0] = self->movedir[1];
  558. self->movedir[1] = temp;
  559. self->movedir[2] *= -1;
  560. }
  561. }
  562. void target_lightramp_use (edict_t *self, edict_t *other, edict_t *activator)
  563. {
  564. if (!self->enemy)
  565. {
  566. edict_t *e;
  567. // check all the targets
  568. e = NULL;
  569. while (1)
  570. {
  571. e = G_Find (e, FOFS(targetname), self->target);
  572. if (!e)
  573. break;
  574. if (strcmp(e->classname, "light") != 0)
  575. {
  576. gi.dprintf("%s at %s ", self->classname, vtos(self->s.origin));
  577. gi.dprintf("target %s (%s at %s) is not a light\n", self->target, e->classname, vtos(e->s.origin));
  578. }
  579. else
  580. {
  581. self->enemy = e;
  582. }
  583. }
  584. if (!self->enemy)
  585. {
  586. gi.dprintf("%s target %s not found at %s\n", self->classname, self->target, vtos(self->s.origin));
  587. G_FreeEdict (self);
  588. return;
  589. }
  590. }
  591. self->timestamp = level.time;
  592. target_lightramp_think (self);
  593. }
  594. void SP_target_lightramp (edict_t *self)
  595. {
  596. 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])
  597. {
  598. gi.dprintf("target_lightramp has bad ramp (%s) at %s\n", self->message, vtos(self->s.origin));
  599. G_FreeEdict (self);
  600. return;
  601. }
  602. if (deathmatch->value)
  603. {
  604. G_FreeEdict (self);
  605. return;
  606. }
  607. if (!self->target)
  608. {
  609. gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
  610. G_FreeEdict (self);
  611. return;
  612. }
  613. self->svflags |= SVF_NOCLIENT;
  614. self->use = target_lightramp_use;
  615. self->think = target_lightramp_think;
  616. self->movedir[0] = self->message[0] - 'a';
  617. self->movedir[1] = self->message[1] - 'a';
  618. self->movedir[2] = (self->movedir[1] - self->movedir[0]) / (self->speed / FRAMETIME);
  619. }
  620. //==========================================================
  621. /*QUAKED target_earthquake (1 0 0) (-8 -8 -8) (8 8 8) SILENT
  622. When triggered, this initiates a level-wide earthquake.
  623. All players and monsters are affected.
  624. "speed" severity of the quake (default:200)
  625. "count" duration of the quake (default:5)
  626. */
  627. void target_earthquake_think (edict_t *self)
  628. {
  629. int i;
  630. edict_t *e;
  631. if(!(self->spawnflags & 1)) // PGM
  632. { // PGM
  633. if (self->last_move_time < level.time)
  634. {
  635. gi.positioned_sound (self->s.origin, self, CHAN_AUTO, self->noise_index, 1.0, ATTN_NONE, 0);
  636. self->last_move_time = level.time + 0.5;
  637. }
  638. } // PGM
  639. for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++)
  640. {
  641. if (!e->inuse)
  642. continue;
  643. if (!e->client)
  644. continue;
  645. if (!e->groundentity)
  646. continue;
  647. e->groundentity = NULL;
  648. e->velocity[0] += crandom()* 150;
  649. e->velocity[1] += crandom()* 150;
  650. e->velocity[2] = self->speed * (100.0 / e->mass);
  651. }
  652. if (level.time < self->timestamp)
  653. self->nextthink = level.time + FRAMETIME;
  654. }
  655. void target_earthquake_use (edict_t *self, edict_t *other, edict_t *activator)
  656. {
  657. // PGM
  658. // if(g_showlogic && g_showlogic->value)
  659. // gi.dprintf("earthquake: %0.1f\n", self->speed);
  660. // PGM
  661. self->timestamp = level.time + self->count;
  662. self->nextthink = level.time + FRAMETIME;
  663. self->activator = activator;
  664. self->last_move_time = 0;
  665. }
  666. void SP_target_earthquake (edict_t *self)
  667. {
  668. if (!self->targetname)
  669. gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
  670. if (!self->count)
  671. self->count = 5;
  672. if (!self->speed)
  673. self->speed = 200;
  674. self->svflags |= SVF_NOCLIENT;
  675. self->think = target_earthquake_think;
  676. self->use = target_earthquake_use;
  677. if(!(self->spawnflags & 1)) // PGM
  678. self->noise_index = gi.soundindex ("world/quake.wav");
  679. }