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