2 void generic_plat_blocked()
\r
4 if(self.dmg && other.takedamage != DAMAGE_NO) {
\r
5 if(self.dmgtime2 < time) {
\r
6 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
7 self.dmgtime2 = time + self.dmgtime;
\r
10 // Gib dead/dying stuff
\r
11 if(other.deadflag != DEAD_NO)
\r
12 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
17 float STATE_TOP = 0;
\r
18 float STATE_BOTTOM = 1;
\r
20 float STATE_DOWN = 3;
\r
22 .entity trigger_field;
\r
24 void() plat_center_touch;
\r
25 void() plat_outside_touch;
\r
26 void() plat_trigger_use;
\r
28 void() plat_go_down;
\r
30 float PLAT_LOW_TRIGGER = 1;
\r
32 void plat_spawn_inside_trigger()
\r
34 local entity trigger;
\r
35 local vector tmin, tmax;
\r
38 trigger.touch = plat_center_touch;
\r
39 trigger.movetype = MOVETYPE_NONE;
\r
40 trigger.solid = SOLID_TRIGGER;
\r
41 trigger.enemy = self;
\r
43 tmin = self.absmin + '25 25 0';
\r
44 tmax = self.absmax - '25 25 -8';
\r
45 tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8);
\r
46 if (self.spawnflags & PLAT_LOW_TRIGGER)
\r
47 tmax_z = tmin_z + 8;
\r
49 if (self.size_x <= 50)
\r
51 tmin_x = (self.mins_x + self.maxs_x) / 2;
\r
52 tmax_x = tmin_x + 1;
\r
54 if (self.size_y <= 50)
\r
56 tmin_y = (self.mins_y + self.maxs_y) / 2;
\r
57 tmax_y = tmin_y + 1;
\r
60 setsize (trigger, tmin, tmax);
\r
65 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
\r
67 self.think = plat_go_down;
\r
68 self.nextthink = self.ltime + 3;
\r
71 void plat_hit_bottom()
\r
73 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
\r
79 sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
\r
81 SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);
\r
86 sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
\r
88 SUB_CalcMove (self.pos1, self.speed, plat_hit_top);
\r
91 void plat_center_touch()
\r
93 if not(other.iscreature)
\r
96 if (other.health <= 0)
\r
100 if (self.state == 2)
\r
102 else if (self.state == 1)
\r
103 self.nextthink = self.ltime + 1; // delay going down
\r
106 void plat_outside_touch()
\r
108 if not(other.iscreature)
\r
111 if (other.health <= 0)
\r
115 if (self.state == 1)
\r
119 void plat_trigger_use()
\r
122 return; // already activated
\r
129 if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
\r
130 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
132 if((self.dmg) && (other.takedamage != DAMAGE_NO)) { // Shall we bite?
\r
133 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
134 // Gib dead/dying stuff
\r
135 if(other.deadflag != DEAD_NO)
\r
136 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
139 if (self.state == 4)
\r
141 else if (self.state == 3)
\r
144 objerror ("plat_crush: bad self.state\n");
\r
150 self.use = SUB_Null;
\r
151 if (self.state != 4)
\r
152 objerror ("plat_use: not in up state");
\r
156 .string sound1, sound2;
\r
162 setorigin (self, self.pos1);
\r
164 self.use = plat_use;
\r
168 setorigin (self, self.pos2);
\r
170 self.use = plat_trigger_use;
\r
174 void spawnfunc_path_corner() { };
\r
175 void spawnfunc_func_plat()
\r
177 if (!self.t_length)
\r
178 self.t_length = 80;
\r
182 if (self.sounds == 0)
\r
185 if(self.spawnflags & 4)
\r
188 if(self.dmg && (!self.message))
\r
189 self.message = "was squished";
\r
190 if(self.dmg && (!self.message2))
\r
191 self.message2 = "was squished by";
\r
193 if (self.sounds == 1)
\r
195 precache_sound ("plats/plat1.wav");
\r
196 precache_sound ("plats/plat2.wav");
\r
197 self.noise = "plats/plat1.wav";
\r
198 self.noise1 = "plats/plat2.wav";
\r
201 if (self.sounds == 2)
\r
203 precache_sound ("plats/medplat1.wav");
\r
204 precache_sound ("plats/medplat2.wav");
\r
205 self.noise = "plats/medplat1.wav";
\r
206 self.noise1 = "plats/medplat2.wav";
\r
211 precache_sound (self.sound1);
\r
212 self.noise = self.sound1;
\r
216 precache_sound (self.sound2);
\r
217 self.noise1 = self.sound2;
\r
220 self.mangle = self.angles;
\r
221 self.angles = '0 0 0';
\r
223 self.classname = "plat";
\r
224 if not(InitMovingBrushTrigger())
\r
226 self.effects |= EF_LOWPRECISION;
\r
227 setsize (self, self.mins , self.maxs);
\r
229 self.blocked = plat_crush;
\r
234 self.pos1 = self.origin;
\r
235 self.pos2 = self.origin;
\r
236 self.pos2_z = self.origin_z - self.size_z + 8;
\r
238 plat_spawn_inside_trigger (); // the "start moving" trigger
\r
240 self.reset = plat_reset;
\r
248 self.think = train_next;
\r
249 self.nextthink = self.ltime + self.wait;
\r
251 if(self.noise != "")
\r
252 stopsoundto(MSG_BROADCAST, self, CHAN_TRIGGER); // send this as unreliable only, as the train will resume operation shortly anyway
\r
258 targ = find(world, targetname, self.target);
\r
259 self.target = targ.target;
\r
261 objerror("train_next: no next target");
\r
262 self.wait = targ.wait;
\r
268 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_next);
\r
270 SUB_CalcMove(targ.origin - self.mins, self.speed, train_next);
\r
275 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
\r
277 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
\r
280 if(self.noise != "")
\r
281 sound(self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE);
\r
284 void func_train_find()
\r
287 targ = find(world, targetname, self.target);
\r
288 self.target = targ.target;
\r
290 objerror("func_train_find: no next target");
\r
291 setorigin(self, targ.origin - self.mins);
\r
292 self.nextthink = self.ltime + 1;
\r
293 self.think = train_next;
\r
296 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
\r
297 Ridable platform, targets spawnfunc_path_corner path to follow.
\r
298 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
\r
299 target : targetname of first spawnfunc_path_corner (starts here)
\r
301 void spawnfunc_func_train()
\r
303 if (self.noise != "")
\r
304 precache_sound(self.noise);
\r
307 objerror("func_train without a target");
\r
311 if not(InitMovingBrushTrigger())
\r
313 self.effects |= EF_LOWPRECISION;
\r
315 // wait for targets to spawn
\r
316 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
\r
318 self.blocked = generic_plat_blocked;
\r
319 if(self.dmg & (!self.message))
\r
320 self.message = " was squished";
\r
321 if(self.dmg && (!self.message2))
\r
322 self.message2 = "was squished by";
\r
323 if(self.dmg && (!self.dmgtime))
\r
324 self.dmgtime = 0.25;
\r
325 self.dmgtime2 = time;
\r
327 // TODO make a reset function for this one
\r
330 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
\r
331 Brush model that spins in place on one axis (default Z).
\r
332 speed : speed to rotate (in degrees per second)
\r
333 noise : path/name of looping .wav file to play.
\r
334 dmg : Do this mutch dmg every .dmgtime intervall when blocked
\r
335 dmgtime : See above.
\r
338 void spawnfunc_func_rotating()
\r
340 if (self.noise != "")
\r
342 precache_sound(self.noise);
\r
343 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
\r
347 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
\r
348 if (self.spawnflags & 4) // X (untested)
\r
349 self.avelocity = '0 0 1' * self.speed;
\r
350 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
\r
351 else if (self.spawnflags & 8) // Y (untested)
\r
352 self.avelocity = '1 0 0' * self.speed;
\r
353 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
\r
355 self.avelocity = '0 1 0' * self.speed;
\r
357 if(self.dmg & (!self.message))
\r
358 self.message = " was squished";
\r
359 if(self.dmg && (!self.message2))
\r
360 self.message2 = "was squished by";
\r
363 if(self.dmg && (!self.dmgtime))
\r
364 self.dmgtime = 0.25;
\r
366 self.dmgtime2 = time;
\r
368 if not(InitMovingBrushTrigger())
\r
370 // no EF_LOWPRECISION here, as rounding angles is bad
\r
372 self.blocked = generic_plat_blocked;
\r
374 // wait for targets to spawn
\r
375 self.nextthink = self.ltime + 999999999;
\r
376 self.think = SUB_Null;
\r
378 // TODO make a reset function for this one
\r
382 void func_bobbing_controller_think()
\r
385 self.nextthink = time + 0.1;
\r
386 // calculate sinewave using makevectors
\r
387 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
\r
388 v = self.owner.destvec + self.owner.movedir * v_forward_y;
\r
389 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
\r
390 // * 10 so it will arrive in 0.1 sec
\r
391 self.owner.velocity = (v - self.owner.origin) * 10;
\r
394 void bobbing_blocked()
\r
396 // no need to duplicate code
\r
397 generic_plat_blocked();
\r
400 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
\r
401 Brush model that moves back and forth on one axis (default Z).
\r
402 speed : how long one cycle takes in seconds (default 4)
\r
403 height : how far the cycle moves (default 32)
\r
404 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
\r
405 noise : path/name of looping .wav file to play.
\r
406 dmg : Do this mutch dmg every .dmgtime intervall when blocked
\r
407 dmgtime : See above.
\r
409 void spawnfunc_func_bobbing()
\r
411 local entity controller;
\r
412 if (self.noise != "")
\r
414 precache_sound(self.noise);
\r
415 soundto(MSG_INIT, self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE, 0);
\r
421 // center of bobbing motion
\r
422 self.destvec = self.origin;
\r
423 // time scale to get degrees
\r
424 self.cnt = 360 / self.speed;
\r
426 // damage when blocked
\r
427 self.blocked = bobbing_blocked;
\r
428 if(self.dmg & (!self.message))
\r
429 self.message = " was squished";
\r
430 if(self.dmg && (!self.message2))
\r
431 self.message2 = "was squished by";
\r
432 if(self.dmg && (!self.dmgtime))
\r
433 self.dmgtime = 0.25;
\r
434 self.dmgtime2 = time;
\r
437 if (self.spawnflags & 1) // X
\r
438 self.movedir = '1 0 0' * self.height;
\r
439 else if (self.spawnflags & 2) // Y
\r
440 self.movedir = '0 1 0' * self.height;
\r
442 self.movedir = '0 0 1' * self.height;
\r
444 if not(InitMovingBrushTrigger())
\r
447 // wait for targets to spawn
\r
448 controller = spawn();
\r
449 controller.classname = "func_bobbing_controller";
\r
450 controller.owner = self;
\r
451 controller.nextthink = time + 1;
\r
452 controller.think = func_bobbing_controller_think;
\r
453 self.nextthink = self.ltime + 999999999;
\r
454 self.think = SUB_Null;
\r
456 // Savage: Reduce bandwith, critical on e.g. nexdm02
\r
457 self.effects |= EF_LOWPRECISION;
\r
459 // TODO make a reset function for this one
\r
462 // button and multiple button
\r
464 void() button_wait;
\r
465 void() button_return;
\r
469 self.state = STATE_TOP;
\r
470 self.nextthink = self.ltime + self.wait;
\r
471 self.think = button_return;
\r
472 activator = self.enemy;
\r
474 self.frame = 1; // use alternate textures
\r
479 self.state = STATE_BOTTOM;
\r
482 void button_return()
\r
484 self.state = STATE_DOWN;
\r
485 SUB_CalcMove (self.pos1, self.speed, button_done);
\r
486 self.frame = 0; // use normal textures
\r
488 self.takedamage = DAMAGE_YES; // can be shot again
\r
492 void button_blocked()
\r
494 // do nothing, just don't come all the way back out
\r
500 self.health = self.max_health;
\r
501 self.takedamage = DAMAGE_NO; // will be reset upon return
\r
503 if (self.state == STATE_UP || self.state == STATE_TOP)
\r
506 if (self.noise != "")
\r
507 sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
\r
509 self.state = STATE_UP;
\r
510 SUB_CalcMove (self.pos2, self.speed, button_wait);
\r
513 void button_reset()
\r
515 self.health = self.max_health;
\r
516 setorigin(self, self.pos1);
\r
517 self.frame = 0; // use normal textures
\r
518 self.state = STATE_BOTTOM;
\r
520 self.takedamage = DAMAGE_YES; // can be shot again
\r
525 // if (activator.classname != "player")
\r
527 // dprint(activator.classname);
\r
528 // dprint(" triggered a button\n");
\r
530 self.enemy = activator;
\r
534 void button_touch()
\r
536 // if (activator.classname != "player")
\r
538 // dprint(activator.classname);
\r
539 // dprint(" touched a button\n");
\r
543 if not(other.iscreature)
\r
545 if(other.velocity * self.movedir < 0)
\r
547 self.enemy = other;
\r
549 self.enemy = other.owner;
\r
553 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
\r
555 if(self.spawnflags & DOOR_NOSPLASH)
\r
556 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
\r
558 self.health = self.health - damage;
\r
559 if (self.health <= 0)
\r
561 // if (activator.classname != "player")
\r
563 // dprint(activator.classname);
\r
564 // dprint(" killed a button\n");
\r
566 self.enemy = damage_attacker;
\r
572 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
\r
573 When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again.
\r
575 "angle" determines the opening direction
\r
576 "target" all entities with a matching targetname will be used
\r
577 "speed" override the default 40 speed
\r
578 "wait" override the default 1 second wait (-1 = never return)
\r
579 "lip" override the default 4 pixel lip remaining at end of move
\r
580 "health" if set, the button must be killed instead of touched. If set to -1, the button will fire on ANY attack, even damageless ones like the MinstaGib laser
\r
587 void spawnfunc_func_button()
\r
591 if not(InitMovingBrushTrigger())
\r
593 self.effects |= EF_LOWPRECISION;
\r
595 self.blocked = button_blocked;
\r
596 self.use = button_use;
\r
598 // if (self.health == 0) // all buttons are now shootable
\r
599 // self.health = 10;
\r
602 self.max_health = self.health;
\r
603 self.event_damage = button_damage;
\r
604 self.takedamage = DAMAGE_YES;
\r
607 self.touch = button_touch;
\r
616 if(self.noise != "")
\r
617 precache_sound(self.noise);
\r
619 self.pos1 = self.origin;
\r
620 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
\r
621 self.flags |= FL_NOTARGET;
\r
627 float DOOR_START_OPEN = 1;
\r
628 float DOOR_DONT_LINK = 4;
\r
629 float DOOR_TOGGLE = 32;
\r
633 Doors are similar to buttons, but can spawn a fat trigger field around them
\r
634 to open without a touch, and they link together to form simultanious
\r
637 Door.owner is the master door. If there is only one door, it points to itself.
\r
638 If multiple doors, all will point to a single one.
\r
640 Door.enemy chains from the master door through all doors linked in the chain.
\r
645 =============================================================================
\r
649 =============================================================================
\r
652 void() door_go_down;
\r
654 void() door_rotating_go_down;
\r
655 void() door_rotating_go_up;
\r
657 void door_blocked()
\r
660 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
\r
661 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
664 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
\r
665 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
667 //Dont chamge direction for dead or dying stuff
\r
668 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
\r
669 if (self.wait >= 0)
\r
671 if (self.state == STATE_DOWN)
\r
672 if (self.classname == "door")
\r
677 door_rotating_go_up ();
\r
680 if (self.classname == "door")
\r
685 door_rotating_go_down ();
\r
689 //gib dying stuff just to make sure
\r
690 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
\r
691 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
695 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
\r
696 // if a door has a negative wait, it would never come back if blocked,
\r
697 // so let it just squash the object to death real fast
\r
698 /* if (self.wait >= 0)
\r
700 if (self.state == STATE_DOWN)
\r
709 void door_hit_top()
\r
711 if (self.noise1 != "")
\r
712 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
\r
713 self.state = STATE_TOP;
\r
714 if (self.spawnflags & DOOR_TOGGLE)
\r
715 return; // don't come down automatically
\r
716 if (self.classname == "door")
\r
718 self.think = door_go_down;
\r
721 self.think = door_rotating_go_down;
\r
723 self.nextthink = self.ltime + self.wait;
\r
726 void door_hit_bottom()
\r
728 if (self.noise1 != "")
\r
729 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
\r
730 self.state = STATE_BOTTOM;
\r
733 void door_go_down()
\r
735 if (self.noise2 != "")
\r
736 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
737 if (self.max_health)
\r
739 self.takedamage = DAMAGE_YES;
\r
740 self.health = self.max_health;
\r
743 self.state = STATE_DOWN;
\r
744 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
\r
749 if (self.state == STATE_UP)
\r
750 return; // already going up
\r
752 if (self.state == STATE_TOP)
\r
753 { // reset top wait time
\r
754 self.nextthink = self.ltime + self.wait;
\r
758 if (self.noise2 != "")
\r
759 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
760 self.state = STATE_UP;
\r
761 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
\r
764 oldmessage = self.message;
\r
767 self.message = oldmessage;
\r
772 =============================================================================
\r
774 ACTIVATION FUNCTIONS
\r
776 =============================================================================
\r
781 local entity oself;
\r
782 local entity starte;
\r
784 if (self.owner != self)
\r
785 objerror ("door_fire: self.owner != self");
\r
789 if (self.spawnflags & DOOR_TOGGLE)
\r
791 if (self.state == STATE_UP || self.state == STATE_TOP)
\r
796 if (self.classname == "door")
\r
802 door_rotating_go_down ();
\r
805 } while ( (self != starte) && (self != world) );
\r
811 // trigger all paired doors
\r
815 if (self.classname == "door")
\r
820 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
\r
821 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
\r
823 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
\r
824 self.pos2 = '0 0 0' - self.pos2;
\r
826 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
\r
827 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
\r
828 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
\r
830 door_rotating_go_up ();
\r
834 } while ( (self != starte) && (self != world) );
\r
841 local entity oself;
\r
843 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
\r
854 void door_trigger_touch()
\r
856 if (other.health < 1)
\r
857 if not(other.iscreature && other.deadflag == DEAD_NO)
\r
860 if (time < self.attack_finished_single)
\r
862 self.attack_finished_single = time + 1;
\r
871 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
\r
873 local entity oself;
\r
874 if(self.spawnflags & DOOR_NOSPLASH)
\r
875 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
\r
877 self.health = self.health - damage;
\r
878 if (self.health <= 0)
\r
882 self.health = self.max_health;
\r
883 self.takedamage = DAMAGE_NO; // wil be reset upon return
\r
899 if(other.classname != "player")
\r
901 if (self.owner.attack_finished_single > time)
\r
904 self.owner.attack_finished_single = time + 2;
\r
906 if (!(self.owner.dmg) && (self.owner.message != ""))
\r
908 if (other.flags & FL_CLIENT)
\r
909 centerprint (other, self.owner.message);
\r
910 play2(other, "misc/talk.wav");
\r
915 void door_generic_plat_blocked()
\r
918 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
\r
919 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
922 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
\r
923 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
925 //Dont chamge direction for dead or dying stuff
\r
926 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
\r
927 if (self.wait >= 0)
\r
929 if (self.state == STATE_DOWN)
\r
930 door_rotating_go_up ();
\r
932 door_rotating_go_down ();
\r
935 //gib dying stuff just to make sure
\r
936 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
\r
937 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
941 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
\r
942 // if a door has a negative wait, it would never come back if blocked,
\r
943 // so let it just squash the object to death real fast
\r
944 /* if (self.wait >= 0)
\r
946 if (self.state == STATE_DOWN)
\r
947 door_rotating_go_up ();
\r
949 door_rotating_go_down ();
\r
955 void door_rotating_hit_top()
\r
957 if (self.noise1 != "")
\r
958 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
\r
959 self.state = STATE_TOP;
\r
960 if (self.spawnflags & DOOR_TOGGLE)
\r
961 return; // don't come down automatically
\r
962 self.think = door_rotating_go_down;
\r
963 self.nextthink = self.ltime + self.wait;
\r
966 void door_rotating_hit_bottom()
\r
968 if (self.noise1 != "")
\r
969 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
\r
970 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
\r
972 self.pos2 = '0 0 0' - self.pos2;
\r
975 self.state = STATE_BOTTOM;
\r
978 void door_rotating_go_down()
\r
980 if (self.noise2 != "")
\r
981 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
982 if (self.max_health)
\r
984 self.takedamage = DAMAGE_YES;
\r
985 self.health = self.max_health;
\r
988 self.state = STATE_DOWN;
\r
989 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
\r
992 void door_rotating_go_up()
\r
994 if (self.state == STATE_UP)
\r
995 return; // already going up
\r
997 if (self.state == STATE_TOP)
\r
998 { // reset top wait time
\r
999 self.nextthink = self.ltime + self.wait;
\r
1002 if (self.noise2 != "")
\r
1003 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
1004 self.state = STATE_UP;
\r
1005 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
\r
1007 string oldmessage;
\r
1008 oldmessage = self.message;
\r
1009 self.message = "";
\r
1011 self.message = oldmessage;
\r
1018 =============================================================================
\r
1020 SPAWNING FUNCTIONS
\r
1022 =============================================================================
\r
1026 entity spawn_field(vector fmins, vector fmaxs)
\r
1028 local entity trigger;
\r
1029 local vector t1, t2;
\r
1031 trigger = spawn();
\r
1032 trigger.classname = "doortriggerfield";
\r
1033 trigger.movetype = MOVETYPE_NONE;
\r
1034 trigger.solid = SOLID_TRIGGER;
\r
1035 trigger.owner = self;
\r
1036 trigger.touch = door_trigger_touch;
\r
1040 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
\r
1045 float EntitiesTouching(entity e1, entity e2)
\r
1047 if (e1.absmin_x > e2.absmax_x)
\r
1049 if (e1.absmin_y > e2.absmax_y)
\r
1051 if (e1.absmin_z > e2.absmax_z)
\r
1053 if (e1.absmax_x < e2.absmin_x)
\r
1055 if (e1.absmax_y < e2.absmin_y)
\r
1057 if (e1.absmax_z < e2.absmin_z)
\r
1072 local entity t, starte;
\r
1073 local vector cmins, cmaxs;
\r
1076 return; // already linked by another door
\r
1077 if (self.spawnflags & 4)
\r
1079 self.owner = self.enemy = self;
\r
1087 self.trigger_field = spawn_field(self.absmin, self.absmax);
\r
1089 return; // don't want to link this door
\r
1092 cmins = self.absmin;
\r
1093 cmaxs = self.absmax;
\r
1100 self.owner = starte; // master door
\r
1103 starte.health = self.health;
\r
1105 starte.targetname = self.targetname;
\r
1106 if (self.message != "")
\r
1107 starte.message = self.message;
\r
1109 t = find(t, classname, self.classname);
\r
1112 self.enemy = starte; // make the chain a loop
\r
1114 // shootable, or triggered doors just needed the owner/enemy links,
\r
1115 // they don't spawn a field
\r
1117 self = self.owner;
\r
1126 self.owner.trigger_field = spawn_field(cmins, cmaxs);
\r
1131 if (EntitiesTouching(self,t))
\r
1134 objerror ("cross connected doors");
\r
1139 if (t.absmin_x < cmins_x)
\r
1140 cmins_x = t.absmin_x;
\r
1141 if (t.absmin_y < cmins_y)
\r
1142 cmins_y = t.absmin_y;
\r
1143 if (t.absmin_z < cmins_z)
\r
1144 cmins_z = t.absmin_z;
\r
1145 if (t.absmax_x > cmaxs_x)
\r
1146 cmaxs_x = t.absmax_x;
\r
1147 if (t.absmax_y > cmaxs_y)
\r
1148 cmaxs_y = t.absmax_y;
\r
1149 if (t.absmax_z > cmaxs_z)
\r
1150 cmaxs_z = t.absmax_z;
\r
1157 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK x x TOGGLE
\r
1158 if two doors touch, they are assumed to be connected and operate as a unit.
\r
1160 TOGGLE causes the door to wait in both the start and end states for a trigger event.
\r
1162 START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors).
\r
1164 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
\r
1165 "angle" determines the opening direction
\r
1166 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
\r
1167 "health" if set, door must be shot open
\r
1168 "speed" movement speed (100 default)
\r
1169 "wait" wait before returning (3 default, -1 = never return)
\r
1170 "lip" lip remaining at end of move (8 default)
\r
1171 "dmg" damage to inflict when blocked (2 default)
\r
1178 FIXME: only one sound set available at the time being
\r
1182 void door_init_startopen()
\r
1184 setorigin (self, self.pos2);
\r
1185 self.pos2 = self.pos1;
\r
1186 self.pos1 = self.origin;
\r
1191 setorigin(self, self.pos1);
\r
1192 self.velocity = '0 0 0';
\r
1193 self.state = STATE_BOTTOM;
\r
1194 self.think = SUB_Null;
\r
1197 void spawnfunc_func_door()
\r
1199 //if (!self.deathtype) // map makers can override this
\r
1200 // self.deathtype = " got in the way";
\r
1203 self.max_health = self.health;
\r
1204 if not(InitMovingBrushTrigger())
\r
1206 self.effects |= EF_LOWPRECISION;
\r
1207 self.classname = "door";
\r
1209 self.blocked = door_blocked;
\r
1210 self.use = door_use;
\r
1212 if(self.spawnflags & 8)
\r
1215 if(self.dmg && (!self.message))
\r
1216 self.message = "was squished";
\r
1217 if(self.dmg && (!self.message2))
\r
1218 self.message2 = "was squished by";
\r
1220 if (self.sounds > 0)
\r
1222 precache_sound ("plats/medplat1.wav");
\r
1223 precache_sound ("plats/medplat2.wav");
\r
1224 self.noise2 = "plats/medplat1.wav";
\r
1225 self.noise1 = "plats/medplat2.wav";
\r
1235 self.pos1 = self.origin;
\r
1236 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
\r
1238 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
\r
1239 // but spawn in the open position
\r
1240 if (self.spawnflags & DOOR_START_OPEN)
\r
1241 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
\r
1243 self.state = STATE_BOTTOM;
\r
1247 self.takedamage = DAMAGE_YES;
\r
1248 self.event_damage = door_damage;
\r
1254 self.touch = door_touch;
\r
1256 // LinkDoors can't be done until all of the doors have been spawned, so
\r
1257 // the sizes can be detected properly.
\r
1258 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
\r
1260 self.reset = door_reset;
\r
1263 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
\r
1264 if two doors touch, they are assumed to be connected and operate as a unit.
\r
1266 TOGGLE causes the door to wait in both the start and end states for a trigger event.
\r
1268 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
\r
1269 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
\r
1270 must have set trigger_reverse to 1.
\r
1271 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
\r
1273 START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors).
\r
1275 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
\r
1276 "angle" determines the destination angle for opening. negative values reverse the direction.
\r
1277 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
\r
1278 "health" if set, door must be shot open
\r
1279 "speed" movement speed (100 default)
\r
1280 "wait" wait before returning (3 default, -1 = never return)
\r
1281 "dmg" damage to inflict when blocked (2 default)
\r
1288 FIXME: only one sound set available at the time being
\r
1291 void door_rotating_reset()
\r
1293 self.angles = self.pos1;
\r
1294 self.avelocity = '0 0 0';
\r
1295 self.state = STATE_BOTTOM;
\r
1296 self.think = SUB_Null;
\r
1299 void door_rotating_init_startopen()
\r
1301 self.angles = self.movedir;
\r
1302 self.pos2 = '0 0 0';
\r
1303 self.pos1 = self.movedir;
\r
1307 void spawnfunc_func_door_rotating()
\r
1310 //if (!self.deathtype) // map makers can override this
\r
1311 // self.deathtype = " got in the way";
\r
1313 // I abuse "movedir" for denoting the axis for now
\r
1314 if (self.spawnflags & 64) // X (untested)
\r
1315 self.movedir = '0 0 1';
\r
1316 else if (self.spawnflags & 128) // Y (untested)
\r
1317 self.movedir = '1 0 0';
\r
1319 self.movedir = '0 1 0';
\r
1321 if (self.angles_y==0) self.angles_y = 90;
\r
1323 self.movedir = self.movedir * self.angles_y;
\r
1324 self.angles = '0 0 0';
\r
1326 self.max_health = self.health;
\r
1327 if not(InitMovingBrushTrigger())
\r
1329 //self.effects |= EF_LOWPRECISION;
\r
1330 self.classname = "door_rotating";
\r
1332 self.blocked = door_blocked;
\r
1333 self.use = door_use;
\r
1335 if(self.spawnflags & 8)
\r
1338 if(self.dmg && (!self.message))
\r
1339 self.message = "was squished";
\r
1340 if(self.dmg && (!self.message2))
\r
1341 self.message2 = "was squished by";
\r
1343 if (self.sounds > 0)
\r
1345 precache_sound ("plats/medplat1.wav");
\r
1346 precache_sound ("plats/medplat2.wav");
\r
1347 self.noise2 = "plats/medplat1.wav";
\r
1348 self.noise1 = "plats/medplat2.wav";
\r
1355 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
\r
1357 self.pos1 = '0 0 0';
\r
1358 self.pos2 = self.movedir;
\r
1360 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
\r
1361 // but spawn in the open position
\r
1362 if (self.spawnflags & DOOR_START_OPEN)
\r
1363 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
\r
1365 self.state = STATE_BOTTOM;
\r
1369 self.takedamage = DAMAGE_YES;
\r
1370 self.event_damage = door_damage;
\r
1376 self.touch = door_touch;
\r
1378 // LinkDoors can't be done until all of the doors have been spawned, so
\r
1379 // the sizes can be detected properly.
\r
1380 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
\r
1382 self.reset = door_rotating_reset;
\r
1386 =============================================================================
\r
1390 =============================================================================
\r
1393 void() fd_secret_move1;
\r
1394 void() fd_secret_move2;
\r
1395 void() fd_secret_move3;
\r
1396 void() fd_secret_move4;
\r
1397 void() fd_secret_move5;
\r
1398 void() fd_secret_move6;
\r
1399 void() fd_secret_done;
\r
1401 float SECRET_OPEN_ONCE = 1; // stays open
\r
1402 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
\r
1403 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
\r
1404 float SECRET_NO_SHOOT = 8; // only opened by trigger
\r
1405 float SECRET_YES_SHOOT = 16; // shootable even if targeted
\r
1407 // This is a bug in the code .. this is the HACK to work around the bug since casts don't exist yet.
\r
1409 void fd_secret_use_core(entity, entity, float, float, vector, vector)
\r
1411 void fd_secret_use();
\r
1412 void fd_secret_use_core() { fd_secret_use(); }
\r
1413 void fd_secret_use()
\r
1417 string message_save;
\r
1419 self.health = 10000;
\r
1420 self.bot_attack = TRUE;
\r
1422 // exit if still moving around...
\r
1423 if (self.origin != self.oldorigin)
\r
1426 message_save = self.message;
\r
1427 self.message = ""; // no more message
\r
1428 SUB_UseTargets(); // fire all targets / killtargets
\r
1429 self.message = message_save;
\r
1431 self.velocity = '0 0 0';
\r
1433 // Make a sound, wait a little...
\r
1435 if (self.noise1 != "")
\r
1436 sound(self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
\r
1437 self.nextthink = self.ltime + 0.1;
\r
1439 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
\r
1440 makevectors(self.mangle);
\r
1442 if (!self.t_width)
\r
1444 if (self.spawnflags & SECRET_1ST_DOWN)
\r
1445 self.t_width = fabs(v_up * self.size);
\r
1447 self.t_width = fabs(v_right * self.size);
\r
1450 if (!self.t_length)
\r
1451 self.t_length = fabs(v_forward * self.size);
\r
1453 if (self.spawnflags & SECRET_1ST_DOWN)
\r
1454 self.dest1 = self.origin - v_up * self.t_width;
\r
1456 self.dest1 = self.origin + v_right * (self.t_width * temp);
\r
1458 self.dest2 = self.dest1 + v_forward * self.t_length;
\r
1459 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
\r
1460 if (self.noise2 != "")
\r
1461 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
1465 void fd_secret_use() {
\r
1466 fd_secret_use_core(nil, nil, nil, nil, nil, nil);
\r
1470 // Wait after first movement...
\r
1471 void fd_secret_move1()
\r
1473 self.nextthink = self.ltime + 1.0;
\r
1474 self.think = fd_secret_move2;
\r
1475 if (self.noise3 != "")
\r
1476 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
\r
1479 // Start moving sideways w/sound...
\r
1480 void fd_secret_move2()
\r
1482 if (self.noise2 != "")
\r
1483 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
1484 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
\r
1487 // Wait here until time to go back...
\r
1488 void fd_secret_move3()
\r
1490 if (self.noise3 != "")
\r
1491 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
\r
1492 if (!(self.spawnflags & SECRET_OPEN_ONCE))
\r
1494 self.nextthink = self.ltime + self.wait;
\r
1495 self.think = fd_secret_move4;
\r
1499 // Move backward...
\r
1500 void fd_secret_move4()
\r
1502 if (self.noise2 != "")
\r
1503 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
1504 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
\r
1507 // Wait 1 second...
\r
1508 void fd_secret_move5()
\r
1510 self.nextthink = self.ltime + 1.0;
\r
1511 self.think = fd_secret_move6;
\r
1512 if (self.noise3 != "")
\r
1513 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
\r
1516 void fd_secret_move6()
\r
1518 if (self.noise2 != "")
\r
1519 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
1520 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
\r
1523 void fd_secret_done()
\r
1525 if (self.spawnflags&SECRET_YES_SHOOT)
\r
1527 self.health = 10000;
\r
1528 self.takedamage = DAMAGE_YES;
\r
1529 //self.th_pain = fd_secret_use;
\r
1531 if (self.noise3 != "")
\r
1532 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
\r
1535 void secret_blocked()
\r
1537 if (time < self.attack_finished_single)
\r
1539 self.attack_finished_single = time + 0.5;
\r
1540 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
\r
1550 void secret_touch()
\r
1552 if not(other.iscreature)
\r
1554 if (self.attack_finished_single > time)
\r
1557 self.attack_finished_single = time + 2;
\r
1561 if (other.flags & FL_CLIENT)
\r
1562 centerprint (other, self.message);
\r
1563 play2(other, "misc/talk.wav");
\r
1567 void secret_reset()
\r
1569 if (self.spawnflags&SECRET_YES_SHOOT)
\r
1571 self.health = 10000;
\r
1572 self.takedamage = DAMAGE_YES;
\r
1574 setorigin(self, self.oldorigin);
\r
1575 self.think = SUB_Null;
\r
1578 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
\r
1579 Basic secret door. Slides back, then to the side. Angle determines direction.
\r
1580 wait = # of seconds before coming back
\r
1581 1st_left = 1st move is left of arrow
\r
1582 1st_down = 1st move is down from arrow
\r
1583 always_shoot = even if targeted, keep shootable
\r
1584 t_width = override WIDTH to move back (or height if going down)
\r
1585 t_length = override LENGTH to move sideways
\r
1586 "dmg" damage to inflict when blocked (2 default)
\r
1588 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
\r
1595 void spawnfunc_func_door_secret()
\r
1597 /*if (!self.deathtype) // map makers can override this
\r
1598 self.deathtype = " got in the way";*/
\r
1603 // Magic formula...
\r
1604 self.mangle = self.angles;
\r
1605 self.angles = '0 0 0';
\r
1606 self.classname = "door";
\r
1607 if not(InitMovingBrushTrigger())
\r
1609 self.effects |= EF_LOWPRECISION;
\r
1611 self.touch = secret_touch;
\r
1612 self.blocked = secret_blocked;
\r
1614 self.use = fd_secret_use;
\r
1619 self.spawnflags |= SECRET_YES_SHOOT;
\r
1621 if(self.spawnflags&SECRET_YES_SHOOT)
\r
1623 self.health = 10000;
\r
1624 self.takedamage = DAMAGE_YES;
\r
1625 self.event_damage = fd_secret_use_core;
\r
1627 self.oldorigin = self.origin;
\r
1629 self.wait = 5; // 5 seconds before closing
\r
1631 self.reset = secret_reset;
\r
1635 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
\r
1636 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
\r
1637 netname: list of <frequencymultiplier> <phase> <x> <y> <z> quadruples, separated by spaces; note that phase 0 represents a sine wave, and phase 0.25 a cosine wave (by default, it uses 1 0 0 0 1, to match func_bobbing's defaults
\r
1638 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
\r
1639 height: amplitude modifier (default 32)
\r
1640 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
\r
1641 noise: path/name of looping .wav file to play.
\r
1642 dmg: Do this mutch dmg every .dmgtime intervall when blocked
\r
1643 dmgtime: See above.
\r
1646 void func_fourier_controller_think()
\r
1651 self.nextthink = time + 0.1;
\r
1653 n = floor((tokenize_console(self.owner.netname)) / 5);
\r
1654 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
\r
1656 v = self.owner.destvec;
\r
1658 for(i = 0; i < n; ++i)
\r
1660 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
\r
1661 v = v + ('1 0 0' * stof(argv(i*5+2)) + '0 1 0' * stof(argv(i*5+3)) + '0 0 1' * stof(argv(i*5+4))) * self.owner.height * v_forward_y;
\r
1664 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
\r
1665 // * 10 so it will arrive in 0.1 sec
\r
1666 self.owner.velocity = (v - self.owner.origin) * 10;
\r
1669 void spawnfunc_func_fourier()
\r
1671 local entity controller;
\r
1672 if (self.noise != "")
\r
1674 precache_sound(self.noise);
\r
1675 soundto(MSG_INIT, self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE, 0);
\r
1682 self.destvec = self.origin;
\r
1683 self.cnt = 360 / self.speed;
\r
1685 self.blocked = generic_plat_blocked;
\r
1686 if(self.dmg & (!self.message))
\r
1687 self.message = " was squished";
\r
1688 if(self.dmg && (!self.message2))
\r
1689 self.message2 = "was squished by";
\r
1690 if(self.dmg && (!self.dmgtime))
\r
1691 self.dmgtime = 0.25;
\r
1692 self.dmgtime2 = time;
\r
1694 if(self.netname == "")
\r
1695 self.netname = "1 0 0 0 1";
\r
1697 if not(InitMovingBrushTrigger())
\r
1700 // wait for targets to spawn
\r
1701 controller = spawn();
\r
1702 controller.classname = "func_fourier_controller";
\r
1703 controller.owner = self;
\r
1704 controller.nextthink = time + 1;
\r
1705 controller.think = func_fourier_controller_think;
\r
1706 self.nextthink = self.ltime + 999999999;
\r
1707 self.think = SUB_Null;
\r
1709 // Savage: Reduce bandwith, critical on e.g. nexdm02
\r
1710 self.effects |= EF_LOWPRECISION;
\r
1712 // TODO make a reset function for this one
\r
1715 // reusing some fields havocbots declared
\r
1716 .entity wp00, wp01, wp02, wp03;
\r
1718 .float targetfactor, target2factor, target3factor, target4factor;
\r
1719 .vector targetnormal, target2normal, target3normal, target4normal;
\r
1721 vector func_vectormamamam_origin(entity o, float t)
\r
1733 p = e.origin + t * e.velocity;
\r
1735 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
\r
1737 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
\r
1743 p = e.origin + t * e.velocity;
\r
1745 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
\r
1747 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
\r
1753 p = e.origin + t * e.velocity;
\r
1755 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
\r
1757 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
\r
1763 p = e.origin + t * e.velocity;
\r
1765 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
\r
1767 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
\r
1773 void func_vectormamamam_controller_think()
\r
1775 self.nextthink = time + 0.1;
\r
1776 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
\r
1777 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
\r
1780 void func_vectormamamam_findtarget()
\r
1782 if(self.target != "")
\r
1783 self.wp00 = find(world, targetname, self.target);
\r
1785 if(self.target2 != "")
\r
1786 self.wp01 = find(world, targetname, self.target2);
\r
1788 if(self.target3 != "")
\r
1789 self.wp02 = find(world, targetname, self.target3);
\r
1791 if(self.target4 != "")
\r
1792 self.wp03 = find(world, targetname, self.target4);
\r
1794 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
\r
1795 objerror("No reference entity found, so there is nothing to move. Aborting.");
\r
1797 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
\r
1799 local entity controller;
\r
1800 controller = spawn();
\r
1801 controller.classname = "func_vectormamamam_controller";
\r
1802 controller.owner = self;
\r
1803 controller.nextthink = time + 1;
\r
1804 controller.think = func_vectormamamam_controller_think;
\r
1807 void spawnfunc_func_vectormamamam()
\r
1809 if (self.noise != "")
\r
1811 precache_sound(self.noise);
\r
1812 soundto(MSG_INIT, self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE, 0);
\r
1815 if(!self.targetfactor)
\r
1816 self.targetfactor = 1;
\r
1818 if(!self.target2factor)
\r
1819 self.target2factor = 1;
\r
1821 if(!self.target3factor)
\r
1822 self.target3factor = 1;
\r
1824 if(!self.target4factor)
\r
1825 self.target4factor = 1;
\r
1827 if(vlen(self.targetnormal))
\r
1828 self.targetnormal = normalize(self.targetnormal);
\r
1830 if(vlen(self.target2normal))
\r
1831 self.target2normal = normalize(self.target2normal);
\r
1833 if(vlen(self.target3normal))
\r
1834 self.target3normal = normalize(self.target3normal);
\r
1836 if(vlen(self.target4normal))
\r
1837 self.target4normal = normalize(self.target4normal);
\r
1839 self.blocked = generic_plat_blocked;
\r
1840 if(self.dmg & (!self.message))
\r
1841 self.message = " was squished";
\r
1842 if(self.dmg && (!self.message2))
\r
1843 self.message2 = "was squished by";
\r
1844 if(self.dmg && (!self.dmgtime))
\r
1845 self.dmgtime = 0.25;
\r
1846 self.dmgtime2 = time;
\r
1848 if(self.netname == "")
\r
1849 self.netname = "1 0 0 0 1";
\r
1851 if not(InitMovingBrushTrigger())
\r
1854 // wait for targets to spawn
\r
1855 self.nextthink = self.ltime + 999999999;
\r
1856 self.think = SUB_Null;
\r
1858 // Savage: Reduce bandwith, critical on e.g. nexdm02
\r
1859 self.effects |= EF_LOWPRECISION;
\r
1861 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
\r