]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/t_plats.qc
Include gmqcc binaries for Windows and Linux
[voretournament/voretournament.git] / data / qcsrc / server / t_plats.qc
1 .float dmgtime2;\r
2 void generic_plat_blocked()\r
3 {\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
8         }\r
9 \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
13     }\r
14 }\r
15 \r
16 \r
17 float   STATE_TOP               = 0;\r
18 float   STATE_BOTTOM    = 1;\r
19 float   STATE_UP                = 2;\r
20 float   STATE_DOWN              = 3;\r
21 \r
22 .entity trigger_field;\r
23 \r
24 void() plat_center_touch;\r
25 void() plat_outside_touch;\r
26 void() plat_trigger_use;\r
27 void() plat_go_up;\r
28 void() plat_go_down;\r
29 void() plat_crush;\r
30 float PLAT_LOW_TRIGGER = 1;\r
31 \r
32 void plat_spawn_inside_trigger()\r
33 {\r
34         local entity trigger;\r
35         local vector tmin, tmax;\r
36 \r
37         trigger = spawn();\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
42 \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
48 \r
49         if (self.size_x <= 50)\r
50         {\r
51                 tmin_x = (self.mins_x + self.maxs_x) / 2;\r
52                 tmax_x = tmin_x + 1;\r
53         }\r
54         if (self.size_y <= 50)\r
55         {\r
56                 tmin_y = (self.mins_y + self.maxs_y) / 2;\r
57                 tmax_y = tmin_y + 1;\r
58         }\r
59 \r
60         setsize (trigger, tmin, tmax);\r
61 };\r
62 \r
63 void plat_hit_top()\r
64 {\r
65         sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);\r
66         self.state = 1;\r
67         self.think = plat_go_down;\r
68         self.nextthink = self.ltime + 3;\r
69 };\r
70 \r
71 void plat_hit_bottom()\r
72 {\r
73         sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);\r
74         self.state = 2;\r
75 };\r
76 \r
77 void plat_go_down()\r
78 {\r
79         sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);\r
80         self.state = 3;\r
81         SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);\r
82 };\r
83 \r
84 void plat_go_up()\r
85 {\r
86         sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);\r
87         self.state = 4;\r
88         SUB_CalcMove (self.pos1, self.speed, plat_hit_top);\r
89 };\r
90 \r
91 void plat_center_touch()\r
92 {\r
93         if not(other.iscreature)\r
94                 return;\r
95 \r
96         if (other.health <= 0)\r
97                 return;\r
98 \r
99         self = self.enemy;\r
100         if (self.state == 2)\r
101                 plat_go_up ();\r
102         else if (self.state == 1)\r
103                 self.nextthink = self.ltime + 1;        // delay going down\r
104 };\r
105 \r
106 void plat_outside_touch()\r
107 {\r
108         if not(other.iscreature)\r
109                 return;\r
110 \r
111         if (other.health <= 0)\r
112                 return;\r
113 \r
114         self = self.enemy;\r
115         if (self.state == 1)\r
116                 plat_go_down ();\r
117 };\r
118 \r
119 void plat_trigger_use()\r
120 {\r
121         if (self.think)\r
122                 return;         // already activated\r
123         plat_go_down();\r
124 };\r
125 \r
126 \r
127 void plat_crush()\r
128 {\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
131     } else {\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
137         }\r
138 \r
139         if (self.state == 4)\r
140             plat_go_down ();\r
141         else if (self.state == 3)\r
142             plat_go_up ();\r
143         else\r
144             objerror ("plat_crush: bad self.state\n");\r
145     }\r
146 };\r
147 \r
148 void plat_use()\r
149 {\r
150         self.use = SUB_Null;\r
151         if (self.state != 4)\r
152                 objerror ("plat_use: not in up state");\r
153         plat_go_down();\r
154 };\r
155 \r
156 .string sound1, sound2;\r
157 \r
158 void plat_reset()\r
159 {\r
160         IFTARGETED\r
161         {\r
162                 setorigin (self, self.pos1);\r
163                 self.state = 4;\r
164                 self.use = plat_use;\r
165         }\r
166         else\r
167         {\r
168                 setorigin (self, self.pos2);\r
169                 self.state = 2;\r
170                 self.use = plat_trigger_use;\r
171         }\r
172 }\r
173 \r
174 void spawnfunc_path_corner() { };\r
175 void spawnfunc_func_plat()\r
176 {\r
177         if (!self.t_length)\r
178                 self.t_length = 80;\r
179         if (!self.t_width)\r
180                 self.t_width = 10;\r
181 \r
182         if (self.sounds == 0)\r
183                 self.sounds = 2;\r
184 \r
185     if(self.spawnflags & 4)\r
186         self.dmg = 10000;\r
187 \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
192 \r
193         if (self.sounds == 1)\r
194         {\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
199         }\r
200 \r
201         if (self.sounds == 2)\r
202         {\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
207         }\r
208 \r
209         if (self.sound1)\r
210         {\r
211                 precache_sound (self.sound1);\r
212                 self.noise = self.sound1;\r
213         }\r
214         if (self.sound2)\r
215         {\r
216                 precache_sound (self.sound2);\r
217                 self.noise1 = self.sound2;\r
218         }\r
219 \r
220         self.mangle = self.angles;\r
221         self.angles = '0 0 0';\r
222 \r
223         self.classname = "plat";\r
224         if not(InitMovingBrushTrigger())\r
225                 return;\r
226         self.effects |= EF_LOWPRECISION;\r
227         setsize (self, self.mins , self.maxs);\r
228 \r
229         self.blocked = plat_crush;\r
230 \r
231         if (!self.speed)\r
232                 self.speed = 150;\r
233 \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
237 \r
238         plat_spawn_inside_trigger ();   // the "start moving" trigger\r
239 \r
240         self.reset = plat_reset;\r
241         plat_reset();\r
242 };\r
243 \r
244 \r
245 void() train_next;\r
246 void train_wait()\r
247 {\r
248         self.think = train_next;\r
249         self.nextthink = self.ltime + self.wait;\r
250 \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
253 };\r
254 \r
255 void train_next()\r
256 {\r
257         local entity targ;\r
258         targ = find(world, targetname, self.target);\r
259         self.target = targ.target;\r
260         if (!self.target)\r
261                 objerror("train_next: no next target");\r
262         self.wait = targ.wait;\r
263         if (!self.wait)\r
264                 self.wait = 0.1;\r
265         if(self.wait < 0)\r
266         {\r
267                 if (targ.speed)\r
268                         SUB_CalcMove(targ.origin - self.mins, targ.speed, train_next);\r
269                 else\r
270                         SUB_CalcMove(targ.origin - self.mins, self.speed, train_next);\r
271         }\r
272         else\r
273         {\r
274                 if (targ.speed)\r
275                         SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);\r
276                 else\r
277                         SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);\r
278         }\r
279 \r
280         if(self.noise != "")\r
281                 sound(self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE);\r
282 };\r
283 \r
284 void func_train_find()\r
285 {\r
286         local entity targ;\r
287         targ = find(world, targetname, self.target);\r
288         self.target = targ.target;\r
289         if (!self.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
294 };\r
295 \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
300 */\r
301 void spawnfunc_func_train()\r
302 {\r
303         if (self.noise != "")\r
304                 precache_sound(self.noise);\r
305 \r
306         if (!self.target)\r
307                 objerror("func_train without a target");\r
308         if (!self.speed)\r
309                 self.speed = 100;\r
310 \r
311         if not(InitMovingBrushTrigger())\r
312                 return;\r
313         self.effects |= EF_LOWPRECISION;\r
314 \r
315         // wait for targets to spawn\r
316         InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);\r
317 \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
326 \r
327         // TODO make a reset function for this one\r
328 };\r
329 \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
336 */\r
337 \r
338 void spawnfunc_func_rotating()\r
339 {\r
340         if (self.noise != "")\r
341         {\r
342                 precache_sound(self.noise);\r
343                 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);\r
344         }\r
345         if (!self.speed)\r
346                 self.speed = 100;\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
354         else // Z\r
355                 self.avelocity = '0 1 0' * self.speed;\r
356 \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
361 \r
362 \r
363     if(self.dmg && (!self.dmgtime))\r
364         self.dmgtime = 0.25;\r
365 \r
366     self.dmgtime2 = time;\r
367 \r
368         if not(InitMovingBrushTrigger())\r
369                 return;\r
370         // no EF_LOWPRECISION here, as rounding angles is bad\r
371 \r
372     self.blocked = generic_plat_blocked;\r
373 \r
374         // wait for targets to spawn\r
375         self.nextthink = self.ltime + 999999999;\r
376         self.think = SUB_Null;\r
377 \r
378         // TODO make a reset function for this one\r
379 };\r
380 \r
381 .float height;\r
382 void func_bobbing_controller_think()\r
383 {\r
384         local vector v;\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
392 };\r
393 \r
394 void bobbing_blocked()\r
395 {\r
396         // no need to duplicate code\r
397         generic_plat_blocked();\r
398 }\r
399 \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
408 */\r
409 void spawnfunc_func_bobbing()\r
410 {\r
411         local entity controller;\r
412         if (self.noise != "")\r
413         {\r
414                 precache_sound(self.noise);\r
415                 soundto(MSG_INIT, self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE, 0);\r
416         }\r
417         if (!self.speed)\r
418                 self.speed = 4;\r
419         if (!self.height)\r
420                 self.height = 32;\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
425 \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
435 \r
436         // how far to bob\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
441         else // Z\r
442                 self.movedir = '0 0 1' * self.height;\r
443 \r
444         if not(InitMovingBrushTrigger())\r
445                 return;\r
446 \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
455 \r
456         // Savage: Reduce bandwith, critical on e.g. nexdm02\r
457         self.effects |= EF_LOWPRECISION;\r
458 \r
459         // TODO make a reset function for this one\r
460 };\r
461 \r
462 // button and multiple button\r
463 \r
464 void() button_wait;\r
465 void() button_return;\r
466 \r
467 void button_wait()\r
468 {\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
473         SUB_UseTargets();\r
474         self.frame = 1;                 // use alternate textures\r
475 };\r
476 \r
477 void button_done()\r
478 {\r
479         self.state = STATE_BOTTOM;\r
480 };\r
481 \r
482 void button_return()\r
483 {\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
487         if (self.health)\r
488                 self.takedamage = DAMAGE_YES;   // can be shot again\r
489 };\r
490 \r
491 \r
492 void button_blocked()\r
493 {\r
494         // do nothing, just don't come all the way back out\r
495 };\r
496 \r
497 \r
498 void button_fire()\r
499 {\r
500         self.health = self.max_health;\r
501         self.takedamage = DAMAGE_NO;    // will be reset upon return\r
502 \r
503         if (self.state == STATE_UP || self.state == STATE_TOP)\r
504                 return;\r
505 \r
506         if (self.noise != "")\r
507                 sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);\r
508 \r
509         self.state = STATE_UP;\r
510         SUB_CalcMove (self.pos2, self.speed, button_wait);\r
511 };\r
512 \r
513 void button_reset()\r
514 {\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
519         if (self.health)\r
520                 self.takedamage = DAMAGE_YES;   // can be shot again\r
521 }\r
522 \r
523 void button_use()\r
524 {\r
525 //      if (activator.classname != "player")\r
526 //      {\r
527 //              dprint(activator.classname);\r
528 //              dprint(" triggered a button\n");\r
529 //      }\r
530         self.enemy = activator;\r
531         button_fire ();\r
532 };\r
533 \r
534 void button_touch()\r
535 {\r
536 //      if (activator.classname != "player")\r
537 //      {\r
538 //              dprint(activator.classname);\r
539 //              dprint(" touched a button\n");\r
540 //      }\r
541         if (!other)\r
542                 return;\r
543         if not(other.iscreature)\r
544                 return;\r
545         if(other.velocity * self.movedir < 0)\r
546                 return;\r
547         self.enemy = other;\r
548         if (other.owner)\r
549                 self.enemy = other.owner;\r
550         button_fire ();\r
551 };\r
552 \r
553 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)\r
554 {\r
555         if(self.spawnflags & DOOR_NOSPLASH)\r
556                 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))\r
557                         return;\r
558         self.health = self.health - damage;\r
559         if (self.health <= 0)\r
560         {\r
561         //      if (activator.classname != "player")\r
562         //      {\r
563         //              dprint(activator.classname);\r
564         //              dprint(" killed a button\n");\r
565         //      }\r
566                 self.enemy = damage_attacker;\r
567                 button_fire ();\r
568         }\r
569 };\r
570 \r
571 \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
574 \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
581 "sounds"\r
582 0) steam metal\r
583 1) wooden clunk\r
584 2) metallic click\r
585 3) in-out\r
586 */\r
587 void spawnfunc_func_button()\r
588 {\r
589         SetMovedir ();\r
590 \r
591         if not(InitMovingBrushTrigger())\r
592                 return;\r
593         self.effects |= EF_LOWPRECISION;\r
594 \r
595         self.blocked = button_blocked;\r
596         self.use = button_use;\r
597 \r
598 //      if (self.health == 0) // all buttons are now shootable\r
599 //              self.health = 10;\r
600         if (self.health)\r
601         {\r
602                 self.max_health = self.health;\r
603                 self.event_damage = button_damage;\r
604                 self.takedamage = DAMAGE_YES;\r
605         }\r
606         else\r
607                 self.touch = button_touch;\r
608 \r
609         if (!self.speed)\r
610                 self.speed = 40;\r
611         if (!self.wait)\r
612                 self.wait = 1;\r
613         if (!self.lip)\r
614                 self.lip = 4;\r
615 \r
616     if(self.noise != "")\r
617         precache_sound(self.noise);\r
618 \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
622 \r
623         button_reset();\r
624 };\r
625 \r
626 \r
627 float DOOR_START_OPEN = 1;\r
628 float DOOR_DONT_LINK = 4;\r
629 float DOOR_TOGGLE = 32;\r
630 \r
631 /*\r
632 \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
635 double/quad doors.\r
636 \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
639 \r
640 Door.enemy chains from the master door through all doors linked in the chain.\r
641 \r
642 */\r
643 \r
644 /*\r
645 =============================================================================\r
646 \r
647 THINK FUNCTIONS\r
648 \r
649 =============================================================================\r
650 */\r
651 \r
652 void() door_go_down;\r
653 void() door_go_up;\r
654 void() door_rotating_go_down;\r
655 void() door_rotating_go_up;\r
656 \r
657 void door_blocked()\r
658 {\r
659 \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
662     } else {\r
663 \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
666 \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
670             {\r
671                 if (self.state == STATE_DOWN)\r
672                         if (self.classname == "door")\r
673                         {\r
674                                 door_go_up ();\r
675                         } else\r
676                         {\r
677                                 door_rotating_go_up ();\r
678                         }\r
679                 else\r
680                         if (self.classname == "door")\r
681                         {\r
682                                 door_go_down ();\r
683                         } else\r
684                         {\r
685                                 door_rotating_go_down ();\r
686                         }\r
687             }\r
688         } else {\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
692         }\r
693     }\r
694 \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
699         {\r
700                 if (self.state == STATE_DOWN)\r
701                         door_go_up ();\r
702                 else\r
703                         door_go_down ();\r
704         }\r
705 */\r
706 };\r
707 \r
708 \r
709 void door_hit_top()\r
710 {\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
717         {\r
718                 self.think = door_go_down;\r
719         } else\r
720         {\r
721                 self.think = door_rotating_go_down;\r
722         }\r
723         self.nextthink = self.ltime + self.wait;\r
724 };\r
725 \r
726 void door_hit_bottom()\r
727 {\r
728         if (self.noise1 != "")\r
729                 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);\r
730         self.state = STATE_BOTTOM;\r
731 };\r
732 \r
733 void door_go_down()\r
734 {\r
735         if (self.noise2 != "")\r
736                 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);\r
737         if (self.max_health)\r
738         {\r
739                 self.takedamage = DAMAGE_YES;\r
740                 self.health = self.max_health;\r
741         }\r
742 \r
743         self.state = STATE_DOWN;\r
744         SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);\r
745 };\r
746 \r
747 void door_go_up()\r
748 {\r
749         if (self.state == STATE_UP)\r
750                 return;         // already going up\r
751 \r
752         if (self.state == STATE_TOP)\r
753         {       // reset top wait time\r
754                 self.nextthink = self.ltime + self.wait;\r
755                 return;\r
756         }\r
757 \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
762 \r
763         string oldmessage;\r
764         oldmessage = self.message;\r
765         self.message = "";\r
766         SUB_UseTargets();\r
767         self.message = oldmessage;\r
768 };\r
769 \r
770 \r
771 /*\r
772 =============================================================================\r
773 \r
774 ACTIVATION FUNCTIONS\r
775 \r
776 =============================================================================\r
777 */\r
778 \r
779 void door_fire()\r
780 {\r
781         local entity    oself;\r
782         local entity    starte;\r
783 \r
784         if (self.owner != self)\r
785                 objerror ("door_fire: self.owner != self");\r
786 \r
787         oself = self;\r
788 \r
789         if (self.spawnflags & DOOR_TOGGLE)\r
790         {\r
791                 if (self.state == STATE_UP || self.state == STATE_TOP)\r
792                 {\r
793                         starte = self;\r
794                         do\r
795                         {\r
796                                 if (self.classname == "door")\r
797                                 {\r
798                                         door_go_down ();\r
799                                 }\r
800                                 else\r
801                                 {\r
802                                         door_rotating_go_down ();\r
803                                 }\r
804                                 self = self.enemy;\r
805                         } while ( (self != starte) && (self != world) );\r
806                         self = oself;\r
807                         return;\r
808                 }\r
809         }\r
810 \r
811 // trigger all paired doors\r
812         starte = self;\r
813         do\r
814         {\r
815                 if (self.classname == "door")\r
816                 {\r
817                         door_go_up ();\r
818                 } else\r
819                 {\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
822                         {\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
825                         }\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
829                         {\r
830                                 door_rotating_go_up ();\r
831                         }\r
832                 }\r
833                 self = self.enemy;\r
834         } while ( (self != starte) && (self != world) );\r
835         self = oself;\r
836 };\r
837 \r
838 \r
839 void door_use()\r
840 {\r
841         local entity oself;\r
842 \r
843         //dprint("door_use (model: ");dprint(self.model);dprint(")\n");\r
844         if (self.owner)\r
845         {\r
846                 oself = self;\r
847                 self = self.owner;\r
848                 door_fire ();\r
849                 self = oself;\r
850         }\r
851 };\r
852 \r
853 \r
854 void door_trigger_touch()\r
855 {\r
856         if (other.health < 1)\r
857         if not(other.iscreature && other.deadflag == DEAD_NO)\r
858                 return;\r
859 \r
860         if (time < self.attack_finished_single)\r
861                 return;\r
862         self.attack_finished_single = time + 1;\r
863 \r
864         activator = other;\r
865 \r
866         self = self.owner;\r
867         door_use ();\r
868 };\r
869 \r
870 \r
871 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)\r
872 {\r
873         local entity oself;\r
874         if(self.spawnflags & DOOR_NOSPLASH)\r
875                 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))\r
876                         return;\r
877         self.health = self.health - damage;\r
878         if (self.health <= 0)\r
879         {\r
880                 oself = self;\r
881                 self = self.owner;\r
882                 self.health = self.max_health;\r
883                 self.takedamage = DAMAGE_NO;    // wil be reset upon return\r
884                 door_use ();\r
885                 self = oself;\r
886         }\r
887 };\r
888 \r
889 \r
890 /*\r
891 ================\r
892 door_touch\r
893 \r
894 Prints messages\r
895 ================\r
896 */\r
897 void door_touch()\r
898 {\r
899         if(other.classname != "player")\r
900                 return;\r
901         if (self.owner.attack_finished_single > time)\r
902                 return;\r
903 \r
904         self.owner.attack_finished_single = time + 2;\r
905 \r
906         if (!(self.owner.dmg) && (self.owner.message != ""))\r
907         {\r
908                 if (other.flags & FL_CLIENT)\r
909                         centerprint (other, self.owner.message);\r
910                 play2(other, "misc/talk.wav");\r
911         }\r
912 };\r
913 \r
914 \r
915 void door_generic_plat_blocked()\r
916 {\r
917 \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
920     } else {\r
921 \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
924 \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
928             {\r
929                 if (self.state == STATE_DOWN)\r
930                     door_rotating_go_up ();\r
931                 else\r
932                     door_rotating_go_down ();\r
933             }\r
934         } else {\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
938         }\r
939     }\r
940 \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
945         {\r
946                 if (self.state == STATE_DOWN)\r
947                         door_rotating_go_up ();\r
948                 else\r
949                         door_rotating_go_down ();\r
950         }\r
951 */\r
952 };\r
953 \r
954 \r
955 void door_rotating_hit_top()\r
956 {\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
964 };\r
965 \r
966 void door_rotating_hit_bottom()\r
967 {\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
971         {\r
972                 self.pos2 = '0 0 0' - self.pos2;\r
973                 self.lip = 0;\r
974         }\r
975         self.state = STATE_BOTTOM;\r
976 };\r
977 \r
978 void door_rotating_go_down()\r
979 {\r
980         if (self.noise2 != "")\r
981                 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);\r
982         if (self.max_health)\r
983         {\r
984                 self.takedamage = DAMAGE_YES;\r
985                 self.health = self.max_health;\r
986         }\r
987 \r
988         self.state = STATE_DOWN;\r
989         SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);\r
990 };\r
991 \r
992 void door_rotating_go_up()\r
993 {\r
994         if (self.state == STATE_UP)\r
995                 return;         // already going up\r
996 \r
997         if (self.state == STATE_TOP)\r
998         {       // reset top wait time\r
999                 self.nextthink = self.ltime + self.wait;\r
1000                 return;\r
1001         }\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
1006 \r
1007         string oldmessage;\r
1008         oldmessage = self.message;\r
1009         self.message = "";\r
1010         SUB_UseTargets();\r
1011         self.message = oldmessage;\r
1012 };\r
1013 \r
1014 \r
1015 \r
1016 \r
1017 /*\r
1018 =============================================================================\r
1019 \r
1020 SPAWNING FUNCTIONS\r
1021 \r
1022 =============================================================================\r
1023 */\r
1024 \r
1025 \r
1026 entity spawn_field(vector fmins, vector fmaxs)\r
1027 {\r
1028         local entity    trigger;\r
1029         local   vector  t1, t2;\r
1030 \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
1037 \r
1038         t1 = fmins;\r
1039         t2 = fmaxs;\r
1040         setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');\r
1041         return (trigger);\r
1042 };\r
1043 \r
1044 \r
1045 float EntitiesTouching(entity e1, entity e2)\r
1046 {\r
1047         if (e1.absmin_x > e2.absmax_x)\r
1048                 return FALSE;\r
1049         if (e1.absmin_y > e2.absmax_y)\r
1050                 return FALSE;\r
1051         if (e1.absmin_z > e2.absmax_z)\r
1052                 return FALSE;\r
1053         if (e1.absmax_x < e2.absmin_x)\r
1054                 return FALSE;\r
1055         if (e1.absmax_y < e2.absmin_y)\r
1056                 return FALSE;\r
1057         if (e1.absmax_z < e2.absmin_z)\r
1058                 return FALSE;\r
1059         return TRUE;\r
1060 };\r
1061 \r
1062 \r
1063 /*\r
1064 =============\r
1065 LinkDoors\r
1066 \r
1067 \r
1068 =============\r
1069 */\r
1070 void LinkDoors()\r
1071 {\r
1072         local entity    t, starte;\r
1073         local vector    cmins, cmaxs;\r
1074 \r
1075         if (self.enemy)\r
1076                 return;         // already linked by another door\r
1077         if (self.spawnflags & 4)\r
1078         {\r
1079                 self.owner = self.enemy = self;\r
1080 \r
1081                 if (self.health)\r
1082                         return;\r
1083                 IFTARGETED\r
1084                         return;\r
1085                 if (self.items)\r
1086                         return;\r
1087                 self.trigger_field = spawn_field(self.absmin, self.absmax);\r
1088 \r
1089                 return;         // don't want to link this door\r
1090         }\r
1091 \r
1092         cmins = self.absmin;\r
1093         cmaxs = self.absmax;\r
1094 \r
1095         starte = self;\r
1096         t = self;\r
1097 \r
1098         do\r
1099         {\r
1100                 self.owner = starte;                    // master door\r
1101 \r
1102                 if (self.health)\r
1103                         starte.health = self.health;\r
1104                 IFTARGETED\r
1105                         starte.targetname = self.targetname;\r
1106                 if (self.message != "")\r
1107                         starte.message = self.message;\r
1108 \r
1109                 t = find(t, classname, self.classname);\r
1110                 if (!t)\r
1111                 {\r
1112                         self.enemy = starte;            // make the chain a loop\r
1113 \r
1114                 // shootable, or triggered doors just needed the owner/enemy links,\r
1115                 // they don't spawn a field\r
1116 \r
1117                         self = self.owner;\r
1118 \r
1119                         if (self.health)\r
1120                                 return;\r
1121                         IFTARGETED\r
1122                                 return;\r
1123                         if (self.items)\r
1124                                 return;\r
1125 \r
1126                         self.owner.trigger_field = spawn_field(cmins, cmaxs);\r
1127 \r
1128                         return;\r
1129                 }\r
1130 \r
1131                 if (EntitiesTouching(self,t))\r
1132                 {\r
1133                         if (t.enemy)\r
1134                                 objerror ("cross connected doors");\r
1135 \r
1136                         self.enemy = t;\r
1137                         self = t;\r
1138 \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
1151                 }\r
1152         } while (1 );\r
1153 \r
1154 };\r
1155 \r
1156 \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
1159 \r
1160 TOGGLE causes the door to wait in both the start and end states for a trigger event.\r
1161 \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
1163 \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
1172 "sounds"\r
1173 0)      no sound\r
1174 1)      stone\r
1175 2)      base\r
1176 3)      stone chain\r
1177 4)      screechy metal\r
1178 FIXME: only one sound set available at the time being\r
1179 \r
1180 */\r
1181 \r
1182 void door_init_startopen()\r
1183 {\r
1184         setorigin (self, self.pos2);\r
1185         self.pos2 = self.pos1;\r
1186         self.pos1 = self.origin;\r
1187 }\r
1188 \r
1189 void door_reset()\r
1190 {\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
1195 }\r
1196 \r
1197 void spawnfunc_func_door()\r
1198 {\r
1199         //if (!self.deathtype) // map makers can override this\r
1200         //      self.deathtype = " got in the way";\r
1201         SetMovedir ();\r
1202 \r
1203         self.max_health = self.health;\r
1204         if not(InitMovingBrushTrigger())\r
1205                 return;\r
1206         self.effects |= EF_LOWPRECISION;\r
1207         self.classname = "door";\r
1208 \r
1209         self.blocked = door_blocked;\r
1210         self.use = door_use;\r
1211 \r
1212     if(self.spawnflags & 8)\r
1213         self.dmg = 10000;\r
1214 \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
1219 \r
1220         if (self.sounds > 0)\r
1221         {\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
1226         }\r
1227 \r
1228         if (!self.speed)\r
1229                 self.speed = 100;\r
1230         if (!self.wait)\r
1231                 self.wait = 3;\r
1232         if (!self.lip)\r
1233                 self.lip = 8;\r
1234 \r
1235         self.pos1 = self.origin;\r
1236         self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);\r
1237 \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
1242 \r
1243         self.state = STATE_BOTTOM;\r
1244 \r
1245         if (self.health)\r
1246         {\r
1247                 self.takedamage = DAMAGE_YES;\r
1248                 self.event_damage = door_damage;\r
1249         }\r
1250 \r
1251         if (self.items)\r
1252                 self.wait = -1;\r
1253 \r
1254         self.touch = door_touch;\r
1255 \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
1259 \r
1260         self.reset = door_reset;\r
1261 };\r
1262 \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
1265 \r
1266 TOGGLE causes the door to wait in both the start and end states for a trigger event.\r
1267 \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
1272 \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
1274 \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
1282 "sounds"\r
1283 0)      no sound\r
1284 1)      stone\r
1285 2)      base\r
1286 3)      stone chain\r
1287 4)      screechy metal\r
1288 FIXME: only one sound set available at the time being\r
1289 */\r
1290 \r
1291 void door_rotating_reset()\r
1292 {\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
1297 }\r
1298 \r
1299 void door_rotating_init_startopen()\r
1300 {\r
1301         self.angles = self.movedir;\r
1302         self.pos2 = '0 0 0';\r
1303         self.pos1 = self.movedir;\r
1304 }\r
1305 \r
1306 \r
1307 void spawnfunc_func_door_rotating()\r
1308 {\r
1309 \r
1310         //if (!self.deathtype) // map makers can override this\r
1311         //      self.deathtype = " got in the way";\r
1312 \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
1318         else // Z\r
1319                 self.movedir = '0 1 0';\r
1320 \r
1321         if (self.angles_y==0) self.angles_y = 90;\r
1322 \r
1323         self.movedir = self.movedir * self.angles_y;\r
1324         self.angles = '0 0 0';\r
1325 \r
1326         self.max_health = self.health;\r
1327         if not(InitMovingBrushTrigger())\r
1328                 return;\r
1329         //self.effects |= EF_LOWPRECISION;\r
1330         self.classname = "door_rotating";\r
1331 \r
1332         self.blocked = door_blocked;\r
1333         self.use = door_use;\r
1334 \r
1335     if(self.spawnflags & 8)\r
1336         self.dmg = 10000;\r
1337 \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
1342 \r
1343     if (self.sounds > 0)\r
1344         {\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
1349         }\r
1350 \r
1351         if (!self.speed)\r
1352                 self.speed = 50;\r
1353         if (!self.wait)\r
1354                 self.wait = 1;\r
1355         self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating\r
1356 \r
1357         self.pos1 = '0 0 0';\r
1358         self.pos2 = self.movedir;\r
1359 \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
1364 \r
1365         self.state = STATE_BOTTOM;\r
1366 \r
1367         if (self.health)\r
1368         {\r
1369                 self.takedamage = DAMAGE_YES;\r
1370                 self.event_damage = door_damage;\r
1371         }\r
1372 \r
1373         if (self.items)\r
1374                 self.wait = -1;\r
1375 \r
1376         self.touch = door_touch;\r
1377 \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
1381 \r
1382         self.reset = door_rotating_reset;\r
1383 };\r
1384 \r
1385 /*\r
1386 =============================================================================\r
1387 \r
1388 SECRET DOORS\r
1389 \r
1390 =============================================================================\r
1391 */\r
1392 \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
1400 \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
1406 \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
1408 #ifdef GMQCC\r
1409 void fd_secret_use_core(entity, entity, float, float, vector, vector)\r
1410 #else\r
1411 void fd_secret_use();\r
1412 void fd_secret_use_core() { fd_secret_use(); }\r
1413 void fd_secret_use()\r
1414 #endif\r
1415 {\r
1416         local float temp;\r
1417         string message_save;\r
1418 \r
1419         self.health = 10000;\r
1420         self.bot_attack = TRUE;\r
1421 \r
1422         // exit if still moving around...\r
1423         if (self.origin != self.oldorigin)\r
1424                 return;\r
1425 \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
1430 \r
1431         self.velocity = '0 0 0';\r
1432 \r
1433         // Make a sound, wait a little...\r
1434 \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
1438 \r
1439         temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1\r
1440         makevectors(self.mangle);\r
1441 \r
1442         if (!self.t_width)\r
1443         {\r
1444                 if (self.spawnflags & SECRET_1ST_DOWN)\r
1445                         self.t_width = fabs(v_up * self.size);\r
1446                 else\r
1447                         self.t_width = fabs(v_right * self.size);\r
1448         }\r
1449 \r
1450         if (!self.t_length)\r
1451                 self.t_length = fabs(v_forward * self.size);\r
1452 \r
1453         if (self.spawnflags & SECRET_1ST_DOWN)\r
1454                 self.dest1 = self.origin - v_up * self.t_width;\r
1455         else\r
1456                 self.dest1 = self.origin + v_right * (self.t_width * temp);\r
1457 \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
1462 };\r
1463 \r
1464 #ifdef GMQCC\r
1465 void fd_secret_use() {\r
1466         fd_secret_use_core(nil, nil, nil, nil, nil, nil);\r
1467 }\r
1468 #endif\r
1469 \r
1470 // Wait after first movement...\r
1471 void fd_secret_move1()\r
1472 {\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
1477 };\r
1478 \r
1479 // Start moving sideways w/sound...\r
1480 void fd_secret_move2()\r
1481 {\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
1485 };\r
1486 \r
1487 // Wait here until time to go back...\r
1488 void fd_secret_move3()\r
1489 {\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
1493         {\r
1494                 self.nextthink = self.ltime + self.wait;\r
1495                 self.think = fd_secret_move4;\r
1496         }\r
1497 };\r
1498 \r
1499 // Move backward...\r
1500 void fd_secret_move4()\r
1501 {\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
1505 };\r
1506 \r
1507 // Wait 1 second...\r
1508 void fd_secret_move5()\r
1509 {\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
1514 };\r
1515 \r
1516 void fd_secret_move6()\r
1517 {\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
1521 };\r
1522 \r
1523 void fd_secret_done()\r
1524 {\r
1525         if (self.spawnflags&SECRET_YES_SHOOT)\r
1526         {\r
1527                 self.health = 10000;\r
1528                 self.takedamage = DAMAGE_YES;\r
1529                 //self.th_pain = fd_secret_use;\r
1530         }\r
1531         if (self.noise3 != "")\r
1532                 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);\r
1533 };\r
1534 \r
1535 void secret_blocked()\r
1536 {\r
1537         if (time < self.attack_finished_single)\r
1538                 return;\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
1541 };\r
1542 \r
1543 /*\r
1544 ==============\r
1545 secret_touch\r
1546 \r
1547 Prints messages\r
1548 ================\r
1549 */\r
1550 void secret_touch()\r
1551 {\r
1552         if not(other.iscreature)\r
1553                 return;\r
1554         if (self.attack_finished_single > time)\r
1555                 return;\r
1556 \r
1557         self.attack_finished_single = time + 2;\r
1558 \r
1559         if (self.message)\r
1560         {\r
1561                 if (other.flags & FL_CLIENT)\r
1562                         centerprint (other, self.message);\r
1563                 play2(other, "misc/talk.wav");\r
1564         }\r
1565 };\r
1566 \r
1567 void secret_reset()\r
1568 {\r
1569         if (self.spawnflags&SECRET_YES_SHOOT)\r
1570         {\r
1571                 self.health = 10000;\r
1572                 self.takedamage = DAMAGE_YES;\r
1573         }\r
1574         setorigin(self, self.oldorigin);\r
1575         self.think = SUB_Null;\r
1576 }\r
1577 \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
1587 \r
1588 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.\r
1589 "sounds"\r
1590 1) medieval\r
1591 2) metal\r
1592 3) base\r
1593 */\r
1594 \r
1595 void spawnfunc_func_door_secret()\r
1596 {\r
1597         /*if (!self.deathtype) // map makers can override this\r
1598                 self.deathtype = " got in the way";*/\r
1599 \r
1600         if (!self.dmg)\r
1601                 self.dmg = 2;\r
1602 \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
1608                 return;\r
1609         self.effects |= EF_LOWPRECISION;\r
1610 \r
1611         self.touch = secret_touch;\r
1612         self.blocked = secret_blocked;\r
1613         self.speed = 50;\r
1614         self.use = fd_secret_use;\r
1615         IFTARGETED\r
1616         {\r
1617         }\r
1618         else\r
1619                 self.spawnflags |= SECRET_YES_SHOOT;\r
1620 \r
1621         if(self.spawnflags&SECRET_YES_SHOOT)\r
1622         {\r
1623                 self.health = 10000;\r
1624                 self.takedamage = DAMAGE_YES;\r
1625                 self.event_damage = fd_secret_use_core;\r
1626         }\r
1627         self.oldorigin = self.origin;\r
1628         if (!self.wait)\r
1629                 self.wait = 5;          // 5 seconds before closing\r
1630 \r
1631         self.reset = secret_reset;\r
1632         secret_reset();\r
1633 };\r
1634 \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
1644 */\r
1645 \r
1646 void func_fourier_controller_think()\r
1647 {\r
1648         local vector v;\r
1649         float n, i, t;\r
1650 \r
1651         self.nextthink = time + 0.1;\r
1652 \r
1653         n = floor((tokenize_console(self.owner.netname)) / 5);\r
1654         t = self.nextthink * self.owner.cnt + self.owner.phase * 360;\r
1655 \r
1656         v = self.owner.destvec;\r
1657 \r
1658         for(i = 0; i < n; ++i)\r
1659         {\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
1662         }\r
1663 \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
1667 };\r
1668 \r
1669 void spawnfunc_func_fourier()\r
1670 {\r
1671         local entity controller;\r
1672         if (self.noise != "")\r
1673         {\r
1674                 precache_sound(self.noise);\r
1675                 soundto(MSG_INIT, self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE, 0);\r
1676         }\r
1677 \r
1678         if (!self.speed)\r
1679                 self.speed = 4;\r
1680         if (!self.height)\r
1681                 self.height = 32;\r
1682         self.destvec = self.origin;\r
1683         self.cnt = 360 / self.speed;\r
1684 \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
1693 \r
1694         if(self.netname == "")\r
1695                 self.netname = "1 0 0 0 1";\r
1696 \r
1697         if not(InitMovingBrushTrigger())\r
1698                 return;\r
1699 \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
1708 \r
1709         // Savage: Reduce bandwith, critical on e.g. nexdm02\r
1710         self.effects |= EF_LOWPRECISION;\r
1711 \r
1712         // TODO make a reset function for this one\r
1713 };\r
1714 \r
1715 // reusing some fields havocbots declared\r
1716 .entity wp00, wp01, wp02, wp03;\r
1717 \r
1718 .float targetfactor, target2factor, target3factor, target4factor;\r
1719 .vector targetnormal, target2normal, target3normal, target4normal;\r
1720 \r
1721 vector func_vectormamamam_origin(entity o, float t)\r
1722 {\r
1723         vector v, p;\r
1724         float f;\r
1725         entity e;\r
1726 \r
1727         f = o.spawnflags;\r
1728         v = '0 0 0';\r
1729 \r
1730         e = o.wp00;\r
1731         if(e)\r
1732         {\r
1733                 p = e.origin + t * e.velocity;\r
1734                 if(f & 1)\r
1735                         v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;\r
1736                 else\r
1737                         v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;\r
1738         }\r
1739 \r
1740         e = o.wp01;\r
1741         if(e)\r
1742         {\r
1743                 p = e.origin + t * e.velocity;\r
1744                 if(f & 2)\r
1745                         v = v + (p * o.target2normal) * o.target2normal * o.target2factor;\r
1746                 else\r
1747                         v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;\r
1748         }\r
1749 \r
1750         e = o.wp02;\r
1751         if(e)\r
1752         {\r
1753                 p = e.origin + t * e.velocity;\r
1754                 if(f & 4)\r
1755                         v = v + (p * o.target3normal) * o.target3normal * o.target3factor;\r
1756                 else\r
1757                         v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;\r
1758         }\r
1759 \r
1760         e = o.wp03;\r
1761         if(e)\r
1762         {\r
1763                 p = e.origin + t * e.velocity;\r
1764                 if(f & 8)\r
1765                         v = v + (p * o.target4normal) * o.target4normal * o.target4factor;\r
1766                 else\r
1767                         v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;\r
1768         }\r
1769 \r
1770         return v;\r
1771 }\r
1772 \r
1773 void func_vectormamamam_controller_think()\r
1774 {\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
1778 }\r
1779 \r
1780 void func_vectormamamam_findtarget()\r
1781 {\r
1782         if(self.target != "")\r
1783                 self.wp00 = find(world, targetname, self.target);\r
1784 \r
1785         if(self.target2 != "")\r
1786                 self.wp01 = find(world, targetname, self.target2);\r
1787 \r
1788         if(self.target3 != "")\r
1789                 self.wp02 = find(world, targetname, self.target3);\r
1790 \r
1791         if(self.target4 != "")\r
1792                 self.wp03 = find(world, targetname, self.target4);\r
1793 \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
1796 \r
1797         self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);\r
1798 \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
1805 }\r
1806 \r
1807 void spawnfunc_func_vectormamamam()\r
1808 {\r
1809         if (self.noise != "")\r
1810         {\r
1811                 precache_sound(self.noise);\r
1812                 soundto(MSG_INIT, self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE, 0);\r
1813         }\r
1814 \r
1815         if(!self.targetfactor)\r
1816                 self.targetfactor = 1;\r
1817 \r
1818         if(!self.target2factor)\r
1819                 self.target2factor = 1;\r
1820 \r
1821         if(!self.target3factor)\r
1822                 self.target3factor = 1;\r
1823 \r
1824         if(!self.target4factor)\r
1825                 self.target4factor = 1;\r
1826 \r
1827         if(vlen(self.targetnormal))\r
1828                 self.targetnormal = normalize(self.targetnormal);\r
1829 \r
1830         if(vlen(self.target2normal))\r
1831                 self.target2normal = normalize(self.target2normal);\r
1832 \r
1833         if(vlen(self.target3normal))\r
1834                 self.target3normal = normalize(self.target3normal);\r
1835 \r
1836         if(vlen(self.target4normal))\r
1837                 self.target4normal = normalize(self.target4normal);\r
1838 \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
1847 \r
1848         if(self.netname == "")\r
1849                 self.netname = "1 0 0 0 1";\r
1850 \r
1851         if not(InitMovingBrushTrigger())\r
1852                 return;\r
1853 \r
1854         // wait for targets to spawn\r
1855         self.nextthink = self.ltime + 999999999;\r
1856         self.think = SUB_Null;\r
1857 \r
1858         // Savage: Reduce bandwith, critical on e.g. nexdm02\r
1859         self.effects |= EF_LOWPRECISION;\r
1860 \r
1861         InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);\r
1862 }\r