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);
\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
1408 void fd_secret_use()
\r
1411 string message_save;
\r
1413 self.health = 10000;
\r
1414 self.bot_attack = TRUE;
\r
1416 // exit if still moving around...
\r
1417 if (self.origin != self.oldorigin)
\r
1420 message_save = self.message;
\r
1421 self.message = ""; // no more message
\r
1422 SUB_UseTargets(); // fire all targets / killtargets
\r
1423 self.message = message_save;
\r
1425 self.velocity = '0 0 0';
\r
1427 // Make a sound, wait a little...
\r
1429 if (self.noise1 != "")
\r
1430 sound(self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
\r
1431 self.nextthink = self.ltime + 0.1;
\r
1433 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
\r
1434 makevectors(self.mangle);
\r
1436 if (!self.t_width)
\r
1438 if (self.spawnflags & SECRET_1ST_DOWN)
\r
1439 self.t_width = fabs(v_up * self.size);
\r
1441 self.t_width = fabs(v_right * self.size);
\r
1444 if (!self.t_length)
\r
1445 self.t_length = fabs(v_forward * self.size);
\r
1447 if (self.spawnflags & SECRET_1ST_DOWN)
\r
1448 self.dest1 = self.origin - v_up * self.t_width;
\r
1450 self.dest1 = self.origin + v_right * (self.t_width * temp);
\r
1452 self.dest2 = self.dest1 + v_forward * self.t_length;
\r
1453 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
\r
1454 if (self.noise2 != "")
\r
1455 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
1458 // Wait after first movement...
\r
1459 void fd_secret_move1()
\r
1461 self.nextthink = self.ltime + 1.0;
\r
1462 self.think = fd_secret_move2;
\r
1463 if (self.noise3 != "")
\r
1464 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
\r
1467 // Start moving sideways w/sound...
\r
1468 void fd_secret_move2()
\r
1470 if (self.noise2 != "")
\r
1471 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
1472 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
\r
1475 // Wait here until time to go back...
\r
1476 void fd_secret_move3()
\r
1478 if (self.noise3 != "")
\r
1479 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
\r
1480 if (!(self.spawnflags & SECRET_OPEN_ONCE))
\r
1482 self.nextthink = self.ltime + self.wait;
\r
1483 self.think = fd_secret_move4;
\r
1487 // Move backward...
\r
1488 void fd_secret_move4()
\r
1490 if (self.noise2 != "")
\r
1491 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
1492 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
\r
1495 // Wait 1 second...
\r
1496 void fd_secret_move5()
\r
1498 self.nextthink = self.ltime + 1.0;
\r
1499 self.think = fd_secret_move6;
\r
1500 if (self.noise3 != "")
\r
1501 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
\r
1504 void fd_secret_move6()
\r
1506 if (self.noise2 != "")
\r
1507 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
\r
1508 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
\r
1511 void fd_secret_done()
\r
1513 if (self.spawnflags&SECRET_YES_SHOOT)
\r
1515 self.health = 10000;
\r
1516 self.takedamage = DAMAGE_YES;
\r
1517 //self.th_pain = fd_secret_use;
\r
1519 if (self.noise3 != "")
\r
1520 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
\r
1523 void secret_blocked()
\r
1525 if (time < self.attack_finished_single)
\r
1527 self.attack_finished_single = time + 0.5;
\r
1528 //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
1538 void secret_touch()
\r
1540 if not(other.iscreature)
\r
1542 if (self.attack_finished_single > time)
\r
1545 self.attack_finished_single = time + 2;
\r
1549 if (other.flags & FL_CLIENT)
\r
1550 centerprint (other, self.message);
\r
1551 play2(other, "misc/talk.wav");
\r
1555 void secret_reset()
\r
1557 if (self.spawnflags&SECRET_YES_SHOOT)
\r
1559 self.health = 10000;
\r
1560 self.takedamage = DAMAGE_YES;
\r
1562 setorigin(self, self.oldorigin);
\r
1563 self.think = SUB_Null;
\r
1566 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
\r
1567 Basic secret door. Slides back, then to the side. Angle determines direction.
\r
1568 wait = # of seconds before coming back
\r
1569 1st_left = 1st move is left of arrow
\r
1570 1st_down = 1st move is down from arrow
\r
1571 always_shoot = even if targeted, keep shootable
\r
1572 t_width = override WIDTH to move back (or height if going down)
\r
1573 t_length = override LENGTH to move sideways
\r
1574 "dmg" damage to inflict when blocked (2 default)
\r
1576 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
\r
1583 void spawnfunc_func_door_secret()
\r
1585 /*if (!self.deathtype) // map makers can override this
\r
1586 self.deathtype = " got in the way";*/
\r
1591 // Magic formula...
\r
1592 self.mangle = self.angles;
\r
1593 self.angles = '0 0 0';
\r
1594 self.classname = "door";
\r
1595 if not(InitMovingBrushTrigger())
\r
1597 self.effects |= EF_LOWPRECISION;
\r
1599 self.touch = secret_touch;
\r
1600 self.blocked = secret_blocked;
\r
1602 self.use = fd_secret_use;
\r
1607 self.spawnflags |= SECRET_YES_SHOOT;
\r
1609 if(self.spawnflags&SECRET_YES_SHOOT)
\r
1611 self.health = 10000;
\r
1612 self.takedamage = DAMAGE_YES;
\r
1613 self.event_damage = fd_secret_use;
\r
1615 self.oldorigin = self.origin;
\r
1617 self.wait = 5; // 5 seconds before closing
\r
1619 self.reset = secret_reset;
\r
1623 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
\r
1624 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
\r
1625 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
1626 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
\r
1627 height: amplitude modifier (default 32)
\r
1628 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
\r
1629 noise: path/name of looping .wav file to play.
\r
1630 dmg: Do this mutch dmg every .dmgtime intervall when blocked
\r
1631 dmgtime: See above.
\r
1634 void func_fourier_controller_think()
\r
1639 self.nextthink = time + 0.1;
\r
1641 n = floor((tokenize_console(self.owner.netname)) / 5);
\r
1642 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
\r
1644 v = self.owner.destvec;
\r
1646 for(i = 0; i < n; ++i)
\r
1648 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
\r
1649 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
1652 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
\r
1653 // * 10 so it will arrive in 0.1 sec
\r
1654 self.owner.velocity = (v - self.owner.origin) * 10;
\r
1657 void spawnfunc_func_fourier()
\r
1659 local entity controller;
\r
1660 if (self.noise != "")
\r
1662 precache_sound(self.noise);
\r
1663 soundto(MSG_INIT, self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE);
\r
1670 self.destvec = self.origin;
\r
1671 self.cnt = 360 / self.speed;
\r
1673 self.blocked = generic_plat_blocked;
\r
1674 if(self.dmg & (!self.message))
\r
1675 self.message = " was squished";
\r
1676 if(self.dmg && (!self.message2))
\r
1677 self.message2 = "was squished by";
\r
1678 if(self.dmg && (!self.dmgtime))
\r
1679 self.dmgtime = 0.25;
\r
1680 self.dmgtime2 = time;
\r
1682 if(self.netname == "")
\r
1683 self.netname = "1 0 0 0 1";
\r
1685 if not(InitMovingBrushTrigger())
\r
1688 // wait for targets to spawn
\r
1689 controller = spawn();
\r
1690 controller.classname = "func_fourier_controller";
\r
1691 controller.owner = self;
\r
1692 controller.nextthink = time + 1;
\r
1693 controller.think = func_fourier_controller_think;
\r
1694 self.nextthink = self.ltime + 999999999;
\r
1695 self.think = SUB_Null;
\r
1697 // Savage: Reduce bandwith, critical on e.g. nexdm02
\r
1698 self.effects |= EF_LOWPRECISION;
\r
1700 // TODO make a reset function for this one
\r
1703 // reusing some fields havocbots declared
\r
1704 .entity wp00, wp01, wp02, wp03;
\r
1706 .float targetfactor, target2factor, target3factor, target4factor;
\r
1707 .vector targetnormal, target2normal, target3normal, target4normal;
\r
1709 vector func_vectormamamam_origin(entity o, float t)
\r
1721 p = e.origin + t * e.velocity;
\r
1723 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
\r
1725 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
\r
1731 p = e.origin + t * e.velocity;
\r
1733 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
\r
1735 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
\r
1741 p = e.origin + t * e.velocity;
\r
1743 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
\r
1745 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
\r
1751 p = e.origin + t * e.velocity;
\r
1753 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
\r
1755 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
\r
1761 void func_vectormamamam_controller_think()
\r
1763 self.nextthink = time + 0.1;
\r
1764 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
\r
1765 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
\r
1768 void func_vectormamamam_findtarget()
\r
1770 if(self.target != "")
\r
1771 self.wp00 = find(world, targetname, self.target);
\r
1773 if(self.target2 != "")
\r
1774 self.wp01 = find(world, targetname, self.target2);
\r
1776 if(self.target3 != "")
\r
1777 self.wp02 = find(world, targetname, self.target3);
\r
1779 if(self.target4 != "")
\r
1780 self.wp03 = find(world, targetname, self.target4);
\r
1782 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
\r
1783 objerror("No reference entity found, so there is nothing to move. Aborting.");
\r
1785 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
\r
1787 local entity controller;
\r
1788 controller = spawn();
\r
1789 controller.classname = "func_vectormamamam_controller";
\r
1790 controller.owner = self;
\r
1791 controller.nextthink = time + 1;
\r
1792 controller.think = func_vectormamamam_controller_think;
\r
1795 void spawnfunc_func_vectormamamam()
\r
1797 if (self.noise != "")
\r
1799 precache_sound(self.noise);
\r
1800 soundto(MSG_INIT, self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE);
\r
1803 if(!self.targetfactor)
\r
1804 self.targetfactor = 1;
\r
1806 if(!self.target2factor)
\r
1807 self.target2factor = 1;
\r
1809 if(!self.target3factor)
\r
1810 self.target3factor = 1;
\r
1812 if(!self.target4factor)
\r
1813 self.target4factor = 1;
\r
1815 if(vlen(self.targetnormal))
\r
1816 self.targetnormal = normalize(self.targetnormal);
\r
1818 if(vlen(self.target2normal))
\r
1819 self.target2normal = normalize(self.target2normal);
\r
1821 if(vlen(self.target3normal))
\r
1822 self.target3normal = normalize(self.target3normal);
\r
1824 if(vlen(self.target4normal))
\r
1825 self.target4normal = normalize(self.target4normal);
\r
1827 self.blocked = generic_plat_blocked;
\r
1828 if(self.dmg & (!self.message))
\r
1829 self.message = " was squished";
\r
1830 if(self.dmg && (!self.message2))
\r
1831 self.message2 = "was squished by";
\r
1832 if(self.dmg && (!self.dmgtime))
\r
1833 self.dmgtime = 0.25;
\r
1834 self.dmgtime2 = time;
\r
1836 if(self.netname == "")
\r
1837 self.netname = "1 0 0 0 1";
\r
1839 if not(InitMovingBrushTrigger())
\r
1842 // wait for targets to spawn
\r
1843 self.nextthink = self.ltime + 999999999;
\r
1844 self.think = SUB_Null;
\r
1846 // Savage: Reduce bandwith, critical on e.g. nexdm02
\r
1847 self.effects |= EF_LOWPRECISION;
\r
1849 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
\r