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 // * 10 so it will arrive in 0.1 sec
\r
390 self.owner.velocity = (v - self.owner.origin) * 10;
\r
393 void bobbing_blocked()
\r
395 // no need to duplicate code
\r
396 generic_plat_blocked();
\r
399 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
\r
400 Brush model that moves back and forth on one axis (default Z).
\r
401 speed : how long one cycle takes in seconds (default 4)
\r
402 height : how far the cycle moves (default 32)
\r
403 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
\r
404 noise : path/name of looping .wav file to play.
\r
405 dmg : Do this mutch dmg every .dmgtime intervall when blocked
\r
406 dmgtime : See above.
\r
408 void spawnfunc_func_bobbing()
\r
410 local entity controller;
\r
411 if (self.noise != "")
\r
413 precache_sound(self.noise);
\r
414 soundto(MSG_INIT, self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE);
\r
420 // center of bobbing motion
\r
421 self.destvec = self.origin;
\r
422 // time scale to get degrees
\r
423 self.cnt = 360 / self.speed;
\r
425 // damage when blocked
\r
426 self.blocked = bobbing_blocked;
\r
427 if(self.dmg & (!self.message))
\r
428 self.message = " was squished";
\r
429 if(self.dmg && (!self.message2))
\r
430 self.message2 = "was squished by";
\r
431 if(self.dmg && (!self.dmgtime))
\r
432 self.dmgtime = 0.25;
\r
433 self.dmgtime2 = time;
\r
436 if (self.spawnflags & 1) // X
\r
437 self.movedir = '1 0 0' * self.height;
\r
438 else if (self.spawnflags & 2) // Y
\r
439 self.movedir = '0 1 0' * self.height;
\r
441 self.movedir = '0 0 1' * self.height;
\r
443 if not(InitMovingBrushTrigger())
\r
446 // wait for targets to spawn
\r
447 controller = spawn();
\r
448 controller.classname = "func_bobbing_controller";
\r
449 controller.owner = self;
\r
450 controller.nextthink = time + 1;
\r
451 controller.think = func_bobbing_controller_think;
\r
452 self.nextthink = self.ltime + 999999999;
\r
453 self.think = SUB_Null;
\r
455 // Savage: Reduce bandwith, critical on e.g. nexdm02
\r
456 self.effects |= EF_LOWPRECISION;
\r
458 // TODO make a reset function for this one
\r
461 // button and multiple button
\r
463 void() button_wait;
\r
464 void() button_return;
\r
468 self.state = STATE_TOP;
\r
469 self.nextthink = self.ltime + self.wait;
\r
470 self.think = button_return;
\r
471 activator = self.enemy;
\r
473 self.frame = 1; // use alternate textures
\r
478 self.state = STATE_BOTTOM;
\r
481 void button_return()
\r
483 self.state = STATE_DOWN;
\r
484 SUB_CalcMove (self.pos1, self.speed, button_done);
\r
485 self.frame = 0; // use normal textures
\r
487 self.takedamage = DAMAGE_YES; // can be shot again
\r
491 void button_blocked()
\r
493 // do nothing, just don't come all the way back out
\r
499 self.health = self.max_health;
\r
500 self.takedamage = DAMAGE_NO; // will be reset upon return
\r
502 if (self.state == STATE_UP || self.state == STATE_TOP)
\r
505 if (self.noise != "")
\r
506 sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
\r
508 self.state = STATE_UP;
\r
509 SUB_CalcMove (self.pos2, self.speed, button_wait);
\r
512 void button_reset()
\r
514 self.health = self.max_health;
\r
515 setorigin(self, self.pos1);
\r
516 self.frame = 0; // use normal textures
\r
517 self.state = STATE_BOTTOM;
\r
519 self.takedamage = DAMAGE_YES; // can be shot again
\r
524 // if (activator.classname != "player")
\r
526 // dprint(activator.classname);
\r
527 // dprint(" triggered a button\n");
\r
529 self.enemy = activator;
\r
533 void button_touch()
\r
535 // if (activator.classname != "player")
\r
537 // dprint(activator.classname);
\r
538 // dprint(" touched a button\n");
\r
542 if not(other.iscreature)
\r
544 if(other.velocity * self.movedir < 0)
\r
546 self.enemy = other;
\r
548 self.enemy = other.owner;
\r
552 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
\r
554 if(self.spawnflags & DOOR_NOSPLASH)
\r
555 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
\r
557 self.health = self.health - damage;
\r
558 if (self.health <= 0)
\r
560 // if (activator.classname != "player")
\r
562 // dprint(activator.classname);
\r
563 // dprint(" killed a button\n");
\r
565 self.enemy = damage_attacker;
\r
571 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
\r
572 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
574 "angle" determines the opening direction
\r
575 "target" all entities with a matching targetname will be used
\r
576 "speed" override the default 40 speed
\r
577 "wait" override the default 1 second wait (-1 = never return)
\r
578 "lip" override the default 4 pixel lip remaining at end of move
\r
579 "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
586 void spawnfunc_func_button()
\r
590 if not(InitMovingBrushTrigger())
\r
592 self.effects |= EF_LOWPRECISION;
\r
594 self.blocked = button_blocked;
\r
595 self.use = button_use;
\r
597 // if (self.health == 0) // all buttons are now shootable
\r
598 // self.health = 10;
\r
601 self.max_health = self.health;
\r
602 self.event_damage = button_damage;
\r
603 self.takedamage = DAMAGE_YES;
\r
606 self.touch = button_touch;
\r
615 if(self.noise != "")
\r
616 precache_sound(self.noise);
\r
618 self.pos1 = self.origin;
\r
619 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
\r
620 self.flags |= FL_NOTARGET;
\r
626 float DOOR_START_OPEN = 1;
\r
627 float DOOR_DONT_LINK = 4;
\r
628 float DOOR_TOGGLE = 32;
\r
632 Doors are similar to buttons, but can spawn a fat trigger field around them
\r
633 to open without a touch, and they link together to form simultanious
\r
636 Door.owner is the master door. If there is only one door, it points to itself.
\r
637 If multiple doors, all will point to a single one.
\r
639 Door.enemy chains from the master door through all doors linked in the chain.
\r
644 =============================================================================
\r
648 =============================================================================
\r
651 void() door_go_down;
\r
653 void() door_rotating_go_down;
\r
654 void() door_rotating_go_up;
\r
656 void door_blocked()
\r
659 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
\r
660 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
663 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
\r
664 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
666 //Dont chamge direction for dead or dying stuff
\r
667 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
\r
668 if (self.wait >= 0)
\r
670 if (self.state == STATE_DOWN)
\r
671 if (self.classname == "door")
\r
676 door_rotating_go_up ();
\r
679 if (self.classname == "door")
\r
684 door_rotating_go_down ();
\r
688 //gib dying stuff just to make sure
\r
689 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
\r
690 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
694 //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
695 // if a door has a negative wait, it would never come back if blocked,
\r
696 // so let it just squash the object to death real fast
\r
697 /* if (self.wait >= 0)
\r
699 if (self.state == STATE_DOWN)
\r
708 void door_hit_top()
\r
710 if (self.noise1 != "")
\r
711 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
\r
712 self.state = STATE_TOP;
\r
713 if (self.spawnflags & DOOR_TOGGLE)
\r
714 return; // don't come down automatically
\r
715 if (self.classname == "door")
\r
717 self.think = door_go_down;
\r
720 self.think = door_rotating_go_down;
\r
722 self.nextthink = self.ltime + self.wait;
\r
725 void door_hit_bottom()
\r
727 if (self.noise1 != "")
\r
728 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
\r
729 self.state = STATE_BOTTOM;
\r
732 void door_go_down()
\r
734 if (self.noise2 != "")
\r
735 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
736 if (self.max_health)
\r
738 self.takedamage = DAMAGE_YES;
\r
739 self.health = self.max_health;
\r
742 self.state = STATE_DOWN;
\r
743 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
\r
748 if (self.state == STATE_UP)
\r
749 return; // already going up
\r
751 if (self.state == STATE_TOP)
\r
752 { // reset top wait time
\r
753 self.nextthink = self.ltime + self.wait;
\r
757 if (self.noise2 != "")
\r
758 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
759 self.state = STATE_UP;
\r
760 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
\r
763 oldmessage = self.message;
\r
766 self.message = oldmessage;
\r
771 =============================================================================
\r
773 ACTIVATION FUNCTIONS
\r
775 =============================================================================
\r
780 local entity oself;
\r
781 local entity starte;
\r
783 if (self.owner != self)
\r
784 objerror ("door_fire: self.owner != self");
\r
788 if (self.spawnflags & DOOR_TOGGLE)
\r
790 if (self.state == STATE_UP || self.state == STATE_TOP)
\r
795 if (self.classname == "door")
\r
801 door_rotating_go_down ();
\r
804 } while ( (self != starte) && (self != world) );
\r
810 // trigger all paired doors
\r
814 if (self.classname == "door")
\r
819 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
\r
820 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
\r
822 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
\r
823 self.pos2 = '0 0 0' - self.pos2;
\r
825 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
\r
826 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
\r
827 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
\r
829 door_rotating_go_up ();
\r
833 } while ( (self != starte) && (self != world) );
\r
840 local entity oself;
\r
842 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
\r
853 void door_trigger_touch()
\r
855 if (other.health < 1)
\r
856 if not(other.iscreature && other.deadflag == DEAD_NO)
\r
859 if (time < self.attack_finished_single)
\r
861 self.attack_finished_single = time + 1;
\r
870 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
\r
872 local entity oself;
\r
873 if(self.spawnflags & DOOR_NOSPLASH)
\r
874 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
\r
876 self.health = self.health - damage;
\r
877 if (self.health <= 0)
\r
881 self.health = self.max_health;
\r
882 self.takedamage = DAMAGE_NO; // wil be reset upon return
\r
898 if(other.classname != "player")
\r
900 if (self.owner.attack_finished_single > time)
\r
903 self.owner.attack_finished_single = time + 2;
\r
905 if (!(self.owner.dmg) && (self.owner.message != ""))
\r
907 if (other.flags & FL_CLIENT)
\r
908 centerprint (other, self.owner.message);
\r
909 play2(other, "misc/talk.wav");
\r
914 void door_generic_plat_blocked()
\r
917 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
\r
918 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
921 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
\r
922 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
924 //Dont chamge direction for dead or dying stuff
\r
925 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
\r
926 if (self.wait >= 0)
\r
928 if (self.state == STATE_DOWN)
\r
929 door_rotating_go_up ();
\r
931 door_rotating_go_down ();
\r
934 //gib dying stuff just to make sure
\r
935 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
\r
936 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
940 //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
941 // if a door has a negative wait, it would never come back if blocked,
\r
942 // so let it just squash the object to death real fast
\r
943 /* if (self.wait >= 0)
\r
945 if (self.state == STATE_DOWN)
\r
946 door_rotating_go_up ();
\r
948 door_rotating_go_down ();
\r
954 void door_rotating_hit_top()
\r
956 if (self.noise1 != "")
\r
957 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
\r
958 self.state = STATE_TOP;
\r
959 if (self.spawnflags & DOOR_TOGGLE)
\r
960 return; // don't come down automatically
\r
961 self.think = door_rotating_go_down;
\r
962 self.nextthink = self.ltime + self.wait;
\r
965 void door_rotating_hit_bottom()
\r
967 if (self.noise1 != "")
\r
968 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
\r
969 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
\r
971 self.pos2 = '0 0 0' - self.pos2;
\r
974 self.state = STATE_BOTTOM;
\r
977 void door_rotating_go_down()
\r
979 if (self.noise2 != "")
\r
980 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
981 if (self.max_health)
\r
983 self.takedamage = DAMAGE_YES;
\r
984 self.health = self.max_health;
\r
987 self.state = STATE_DOWN;
\r
988 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
\r
991 void door_rotating_go_up()
\r
993 if (self.state == STATE_UP)
\r
994 return; // already going up
\r
996 if (self.state == STATE_TOP)
\r
997 { // reset top wait time
\r
998 self.nextthink = self.ltime + self.wait;
\r
1001 if (self.noise2 != "")
\r
1002 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
1003 self.state = STATE_UP;
\r
1004 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
\r
1006 string oldmessage;
\r
1007 oldmessage = self.message;
\r
1008 self.message = "";
\r
1010 self.message = oldmessage;
\r
1017 =============================================================================
\r
1019 SPAWNING FUNCTIONS
\r
1021 =============================================================================
\r
1025 entity spawn_field(vector fmins, vector fmaxs)
\r
1027 local entity trigger;
\r
1028 local vector t1, t2;
\r
1030 trigger = spawn();
\r
1031 trigger.classname = "doortriggerfield";
\r
1032 trigger.movetype = MOVETYPE_NONE;
\r
1033 trigger.solid = SOLID_TRIGGER;
\r
1034 trigger.owner = self;
\r
1035 trigger.touch = door_trigger_touch;
\r
1039 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
\r
1044 float EntitiesTouching(entity e1, entity e2)
\r
1046 if (e1.absmin_x > e2.absmax_x)
\r
1048 if (e1.absmin_y > e2.absmax_y)
\r
1050 if (e1.absmin_z > e2.absmax_z)
\r
1052 if (e1.absmax_x < e2.absmin_x)
\r
1054 if (e1.absmax_y < e2.absmin_y)
\r
1056 if (e1.absmax_z < e2.absmin_z)
\r
1071 local entity t, starte;
\r
1072 local vector cmins, cmaxs;
\r
1075 return; // already linked by another door
\r
1076 if (self.spawnflags & 4)
\r
1078 self.owner = self.enemy = self;
\r
1086 self.trigger_field = spawn_field(self.absmin, self.absmax);
\r
1088 return; // don't want to link this door
\r
1091 cmins = self.absmin;
\r
1092 cmaxs = self.absmax;
\r
1099 self.owner = starte; // master door
\r
1102 starte.health = self.health;
\r
1104 starte.targetname = self.targetname;
\r
1105 if (self.message != "")
\r
1106 starte.message = self.message;
\r
1108 t = find(t, classname, self.classname);
\r
1111 self.enemy = starte; // make the chain a loop
\r
1113 // shootable, or triggered doors just needed the owner/enemy links,
\r
1114 // they don't spawn a field
\r
1116 self = self.owner;
\r
1125 self.owner.trigger_field = spawn_field(cmins, cmaxs);
\r
1130 if (EntitiesTouching(self,t))
\r
1133 objerror ("cross connected doors");
\r
1138 if (t.absmin_x < cmins_x)
\r
1139 cmins_x = t.absmin_x;
\r
1140 if (t.absmin_y < cmins_y)
\r
1141 cmins_y = t.absmin_y;
\r
1142 if (t.absmin_z < cmins_z)
\r
1143 cmins_z = t.absmin_z;
\r
1144 if (t.absmax_x > cmaxs_x)
\r
1145 cmaxs_x = t.absmax_x;
\r
1146 if (t.absmax_y > cmaxs_y)
\r
1147 cmaxs_y = t.absmax_y;
\r
1148 if (t.absmax_z > cmaxs_z)
\r
1149 cmaxs_z = t.absmax_z;
\r
1156 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK x x TOGGLE
\r
1157 if two doors touch, they are assumed to be connected and operate as a unit.
\r
1159 TOGGLE causes the door to wait in both the start and end states for a trigger event.
\r
1161 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
1163 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
\r
1164 "angle" determines the opening direction
\r
1165 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
\r
1166 "health" if set, door must be shot open
\r
1167 "speed" movement speed (100 default)
\r
1168 "wait" wait before returning (3 default, -1 = never return)
\r
1169 "lip" lip remaining at end of move (8 default)
\r
1170 "dmg" damage to inflict when blocked (2 default)
\r
1177 FIXME: only one sound set available at the time being
\r
1181 void door_init_startopen()
\r
1183 setorigin (self, self.pos2);
\r
1184 self.pos2 = self.pos1;
\r
1185 self.pos1 = self.origin;
\r
1190 setorigin(self, self.pos1);
\r
1191 self.velocity = '0 0 0';
\r
1192 self.state = STATE_BOTTOM;
\r
1193 self.think = SUB_Null;
\r
1196 void spawnfunc_func_door()
\r
1198 //if (!self.deathtype) // map makers can override this
\r
1199 // self.deathtype = " got in the way";
\r
1202 self.max_health = self.health;
\r
1203 if not(InitMovingBrushTrigger())
\r
1205 self.effects |= EF_LOWPRECISION;
\r
1206 self.classname = "door";
\r
1208 self.blocked = door_blocked;
\r
1209 self.use = door_use;
\r
1211 if(self.spawnflags & 8)
\r
1214 if(self.dmg && (!self.message))
\r
1215 self.message = "was squished";
\r
1216 if(self.dmg && (!self.message2))
\r
1217 self.message2 = "was squished by";
\r
1219 if (self.sounds > 0)
\r
1221 precache_sound ("plats/medplat1.wav");
\r
1222 precache_sound ("plats/medplat2.wav");
\r
1223 self.noise2 = "plats/medplat1.wav";
\r
1224 self.noise1 = "plats/medplat2.wav";
\r
1234 self.pos1 = self.origin;
\r
1235 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
\r
1237 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
\r
1238 // but spawn in the open position
\r
1239 if (self.spawnflags & DOOR_START_OPEN)
\r
1240 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
\r
1242 self.state = STATE_BOTTOM;
\r
1246 self.takedamage = DAMAGE_YES;
\r
1247 self.event_damage = door_damage;
\r
1253 self.touch = door_touch;
\r
1255 // LinkDoors can't be done until all of the doors have been spawned, so
\r
1256 // the sizes can be detected properly.
\r
1257 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
\r
1259 self.reset = door_reset;
\r
1262 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
\r
1263 if two doors touch, they are assumed to be connected and operate as a unit.
\r
1265 TOGGLE causes the door to wait in both the start and end states for a trigger event.
\r
1267 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
\r
1268 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
\r
1269 must have set trigger_reverse to 1.
\r
1270 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
\r
1272 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
1274 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
\r
1275 "angle" determines the destination angle for opening. negative values reverse the direction.
\r
1276 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
\r
1277 "health" if set, door must be shot open
\r
1278 "speed" movement speed (100 default)
\r
1279 "wait" wait before returning (3 default, -1 = never return)
\r
1280 "dmg" damage to inflict when blocked (2 default)
\r
1287 FIXME: only one sound set available at the time being
\r
1290 void door_rotating_reset()
\r
1292 self.angles = self.pos1;
\r
1293 self.avelocity = '0 0 0';
\r
1294 self.state = STATE_BOTTOM;
\r
1295 self.think = SUB_Null;
\r
1298 void door_rotating_init_startopen()
\r
1300 self.angles = self.movedir;
\r
1301 self.pos2 = '0 0 0';
\r
1302 self.pos1 = self.movedir;
\r
1306 void spawnfunc_func_door_rotating()
\r
1309 //if (!self.deathtype) // map makers can override this
\r
1310 // self.deathtype = " got in the way";
\r
1312 // I abuse "movedir" for denoting the axis for now
\r
1313 if (self.spawnflags & 64) // X (untested)
\r
1314 self.movedir = '0 0 1';
\r
1315 else if (self.spawnflags & 128) // Y (untested)
\r
1316 self.movedir = '1 0 0';
\r
1318 self.movedir = '0 1 0';
\r
1320 if (self.angles_y==0) self.angles_y = 90;
\r
1322 self.movedir = self.movedir * self.angles_y;
\r
1323 self.angles = '0 0 0';
\r
1325 self.max_health = self.health;
\r
1326 if not(InitMovingBrushTrigger())
\r
1328 //self.effects |= EF_LOWPRECISION;
\r
1329 self.classname = "door_rotating";
\r
1331 self.blocked = door_blocked;
\r
1332 self.use = door_use;
\r
1334 if(self.spawnflags & 8)
\r
1337 if(self.dmg && (!self.message))
\r
1338 self.message = "was squished";
\r
1339 if(self.dmg && (!self.message2))
\r
1340 self.message2 = "was squished by";
\r
1342 if (self.sounds > 0)
\r
1344 precache_sound ("plats/medplat1.wav");
\r
1345 precache_sound ("plats/medplat2.wav");
\r
1346 self.noise2 = "plats/medplat1.wav";
\r
1347 self.noise1 = "plats/medplat2.wav";
\r
1354 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
\r
1356 self.pos1 = '0 0 0';
\r
1357 self.pos2 = self.movedir;
\r
1359 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
\r
1360 // but spawn in the open position
\r
1361 if (self.spawnflags & DOOR_START_OPEN)
\r
1362 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
\r
1364 self.state = STATE_BOTTOM;
\r
1368 self.takedamage = DAMAGE_YES;
\r
1369 self.event_damage = door_damage;
\r
1375 self.touch = door_touch;
\r
1377 // LinkDoors can't be done until all of the doors have been spawned, so
\r
1378 // the sizes can be detected properly.
\r
1379 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
\r
1381 self.reset = door_rotating_reset;
\r
1385 =============================================================================
\r
1389 =============================================================================
\r
1392 void() fd_secret_move1;
\r
1393 void() fd_secret_move2;
\r
1394 void() fd_secret_move3;
\r
1395 void() fd_secret_move4;
\r
1396 void() fd_secret_move5;
\r
1397 void() fd_secret_move6;
\r
1398 void() fd_secret_done;
\r
1400 float SECRET_OPEN_ONCE = 1; // stays open
\r
1401 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
\r
1402 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
\r
1403 float SECRET_NO_SHOOT = 8; // only opened by trigger
\r
1404 float SECRET_YES_SHOOT = 16; // shootable even if targeted
\r
1407 void fd_secret_use()
\r
1410 string message_save;
\r
1412 self.health = 10000;
\r
1413 self.bot_attack = TRUE;
\r
1415 // exit if still moving around...
\r
1416 if (self.origin != self.oldorigin)
\r
1419 message_save = self.message;
\r
1420 self.message = ""; // no more message
\r
1421 SUB_UseTargets(); // fire all targets / killtargets
\r
1422 self.message = message_save;
\r
1424 self.velocity = '0 0 0';
\r
1426 // Make a sound, wait a little...
\r
1428 if (self.noise1 != "")
\r
1429 sound(self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
\r
1430 self.nextthink = self.ltime + 0.1;
\r
1432 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
\r
1433 makevectors(self.mangle);
\r
1435 if (!self.t_width)
\r
1437 if (self.spawnflags & SECRET_1ST_DOWN)
\r
1438 self.t_width = fabs(v_up * self.size);
\r
1440 self.t_width = fabs(v_right * self.size);
\r
1443 if (!self.t_length)
\r
1444 self.t_length = fabs(v_forward * self.size);
\r
1446 if (self.spawnflags & SECRET_1ST_DOWN)
\r
1447 self.dest1 = self.origin - v_up * self.t_width;
\r
1449 self.dest1 = self.origin + v_right * (self.t_width * temp);
\r
1451 self.dest2 = self.dest1 + v_forward * self.t_length;
\r
1452 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
\r
1453 if (self.noise2 != "")
\r
1454 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
1457 // Wait after first movement...
\r
1458 void fd_secret_move1()
\r
1460 self.nextthink = self.ltime + 1.0;
\r
1461 self.think = fd_secret_move2;
\r
1462 if (self.noise3 != "")
\r
1463 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
\r
1466 // Start moving sideways w/sound...
\r
1467 void fd_secret_move2()
\r
1469 if (self.noise2 != "")
\r
1470 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
1471 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
\r
1474 // Wait here until time to go back...
\r
1475 void fd_secret_move3()
\r
1477 if (self.noise3 != "")
\r
1478 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
\r
1479 if (!(self.spawnflags & SECRET_OPEN_ONCE))
\r
1481 self.nextthink = self.ltime + self.wait;
\r
1482 self.think = fd_secret_move4;
\r
1486 // Move backward...
\r
1487 void fd_secret_move4()
\r
1489 if (self.noise2 != "")
\r
1490 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
1491 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
\r
1494 // Wait 1 second...
\r
1495 void fd_secret_move5()
\r
1497 self.nextthink = self.ltime + 1.0;
\r
1498 self.think = fd_secret_move6;
\r
1499 if (self.noise3 != "")
\r
1500 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
\r
1503 void fd_secret_move6()
\r
1505 if (self.noise2 != "")
\r
1506 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
1507 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
\r
1510 void fd_secret_done()
\r
1512 if (self.spawnflags&SECRET_YES_SHOOT)
\r
1514 self.health = 10000;
\r
1515 self.takedamage = DAMAGE_YES;
\r
1516 //self.th_pain = fd_secret_use;
\r
1518 if (self.noise3 != "")
\r
1519 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
\r
1522 void secret_blocked()
\r
1524 if (time < self.attack_finished_single)
\r
1526 self.attack_finished_single = time + 0.5;
\r
1527 //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
1537 void secret_touch()
\r
1539 if not(other.iscreature)
\r
1541 if (self.attack_finished_single > time)
\r
1544 self.attack_finished_single = time + 2;
\r
1548 if (other.flags & FL_CLIENT)
\r
1549 centerprint (other, self.message);
\r
1550 play2(other, "misc/talk.wav");
\r
1554 void secret_reset()
\r
1556 if (self.spawnflags&SECRET_YES_SHOOT)
\r
1558 self.health = 10000;
\r
1559 self.takedamage = DAMAGE_YES;
\r
1561 setorigin(self, self.oldorigin);
\r
1562 self.think = SUB_Null;
\r
1565 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
\r
1566 Basic secret door. Slides back, then to the side. Angle determines direction.
\r
1567 wait = # of seconds before coming back
\r
1568 1st_left = 1st move is left of arrow
\r
1569 1st_down = 1st move is down from arrow
\r
1570 always_shoot = even if targeted, keep shootable
\r
1571 t_width = override WIDTH to move back (or height if going down)
\r
1572 t_length = override LENGTH to move sideways
\r
1573 "dmg" damage to inflict when blocked (2 default)
\r
1575 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
\r
1582 void spawnfunc_func_door_secret()
\r
1584 /*if (!self.deathtype) // map makers can override this
\r
1585 self.deathtype = " got in the way";*/
\r
1590 // Magic formula...
\r
1591 self.mangle = self.angles;
\r
1592 self.angles = '0 0 0';
\r
1593 self.classname = "door";
\r
1594 if not(InitMovingBrushTrigger())
\r
1596 self.effects |= EF_LOWPRECISION;
\r
1598 self.touch = secret_touch;
\r
1599 self.blocked = secret_blocked;
\r
1601 self.use = fd_secret_use;
\r
1606 self.spawnflags |= SECRET_YES_SHOOT;
\r
1608 if(self.spawnflags&SECRET_YES_SHOOT)
\r
1610 self.health = 10000;
\r
1611 self.takedamage = DAMAGE_YES;
\r
1612 self.event_damage = fd_secret_use;
\r
1614 self.oldorigin = self.origin;
\r
1616 self.wait = 5; // 5 seconds before closing
\r
1618 self.reset = secret_reset;
\r
1622 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
\r
1623 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
\r
1624 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
1625 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
\r
1626 height: amplitude modifier (default 32)
\r
1627 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
\r
1628 noise: path/name of looping .wav file to play.
\r
1629 dmg: Do this mutch dmg every .dmgtime intervall when blocked
\r
1630 dmgtime: See above.
\r
1633 void func_fourier_controller_think()
\r
1638 self.nextthink = time + 0.1;
\r
1640 n = floor((tokenize_console(self.owner.netname)) / 5);
\r
1641 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
\r
1643 v = self.owner.destvec;
\r
1645 for(i = 0; i < n; ++i)
\r
1647 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
\r
1648 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
1651 // * 10 so it will arrive in 0.1 sec
\r
1652 self.owner.velocity = (v - self.owner.origin) * 10;
\r
1655 void spawnfunc_func_fourier()
\r
1657 local entity controller;
\r
1658 if (self.noise != "")
\r
1660 precache_sound(self.noise);
\r
1661 soundto(MSG_INIT, self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE);
\r
1668 self.destvec = self.origin;
\r
1669 self.cnt = 360 / self.speed;
\r
1671 self.blocked = generic_plat_blocked;
\r
1672 if(self.dmg & (!self.message))
\r
1673 self.message = " was squished";
\r
1674 if(self.dmg && (!self.message2))
\r
1675 self.message2 = "was squished by";
\r
1676 if(self.dmg && (!self.dmgtime))
\r
1677 self.dmgtime = 0.25;
\r
1678 self.dmgtime2 = time;
\r
1680 if(self.netname == "")
\r
1681 self.netname = "1 0 0 0 1";
\r
1683 if not(InitMovingBrushTrigger())
\r
1686 // wait for targets to spawn
\r
1687 controller = spawn();
\r
1688 controller.classname = "func_fourier_controller";
\r
1689 controller.owner = self;
\r
1690 controller.nextthink = time + 1;
\r
1691 controller.think = func_fourier_controller_think;
\r
1692 self.nextthink = self.ltime + 999999999;
\r
1693 self.think = SUB_Null;
\r
1695 // Savage: Reduce bandwith, critical on e.g. nexdm02
\r
1696 self.effects |= EF_LOWPRECISION;
\r
1698 // TODO make a reset function for this one
\r
1701 // reusing some fields havocbots declared
\r
1702 .entity wp00, wp01, wp02, wp03;
\r
1704 .float targetfactor, target2factor, target3factor, target4factor;
\r
1705 .vector targetnormal, target2normal, target3normal, target4normal;
\r
1707 vector func_vectormamamam_origin(entity o, float t)
\r
1719 p = e.origin + t * e.velocity;
\r
1721 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
\r
1723 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
\r
1729 p = e.origin + t * e.velocity;
\r
1731 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
\r
1733 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
\r
1739 p = e.origin + t * e.velocity;
\r
1741 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
\r
1743 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
\r
1749 p = e.origin + t * e.velocity;
\r
1751 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
\r
1753 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
\r
1759 void func_vectormamamam_controller_think()
\r
1761 self.nextthink = time + 0.1;
\r
1762 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
\r
1765 void func_vectormamamam_findtarget()
\r
1767 if(self.target != "")
\r
1768 self.wp00 = find(world, targetname, self.target);
\r
1770 if(self.target2 != "")
\r
1771 self.wp01 = find(world, targetname, self.target2);
\r
1773 if(self.target3 != "")
\r
1774 self.wp02 = find(world, targetname, self.target3);
\r
1776 if(self.target4 != "")
\r
1777 self.wp03 = find(world, targetname, self.target4);
\r
1779 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
\r
1780 objerror("No reference entity found, so there is nothing to move. Aborting.");
\r
1782 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
\r
1784 local entity controller;
\r
1785 controller = spawn();
\r
1786 controller.classname = "func_vectormamamam_controller";
\r
1787 controller.owner = self;
\r
1788 controller.nextthink = time + 1;
\r
1789 controller.think = func_vectormamamam_controller_think;
\r
1792 void spawnfunc_func_vectormamamam()
\r
1794 if (self.noise != "")
\r
1796 precache_sound(self.noise);
\r
1797 soundto(MSG_INIT, self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE);
\r
1800 if(!self.targetfactor)
\r
1801 self.targetfactor = 1;
\r
1803 if(!self.target2factor)
\r
1804 self.target2factor = 1;
\r
1806 if(!self.target3factor)
\r
1807 self.target3factor = 1;
\r
1809 if(!self.target4factor)
\r
1810 self.target4factor = 1;
\r
1812 if(vlen(self.targetnormal))
\r
1813 self.targetnormal = normalize(self.targetnormal);
\r
1815 if(vlen(self.target2normal))
\r
1816 self.target2normal = normalize(self.target2normal);
\r
1818 if(vlen(self.target3normal))
\r
1819 self.target3normal = normalize(self.target3normal);
\r
1821 if(vlen(self.target4normal))
\r
1822 self.target4normal = normalize(self.target4normal);
\r
1824 self.blocked = generic_plat_blocked;
\r
1825 if(self.dmg & (!self.message))
\r
1826 self.message = " was squished";
\r
1827 if(self.dmg && (!self.message2))
\r
1828 self.message2 = "was squished by";
\r
1829 if(self.dmg && (!self.dmgtime))
\r
1830 self.dmgtime = 0.25;
\r
1831 self.dmgtime2 = time;
\r
1833 if(self.netname == "")
\r
1834 self.netname = "1 0 0 0 1";
\r
1836 if not(InitMovingBrushTrigger())
\r
1839 // wait for targets to spawn
\r
1840 self.nextthink = self.ltime + 999999999;
\r
1841 self.think = SUB_Null;
\r
1843 // Savage: Reduce bandwith, critical on e.g. nexdm02
\r
1844 self.effects |= EF_LOWPRECISION;
\r
1846 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
\r