]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/triggers/func/door.qc
Merge branch 'master' into Mario/use1
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / triggers / func / door.qc
1 /*
2
3 Doors are similar to buttons, but can spawn a fat trigger field around them
4 to open without a touch, and they link together to form simultanious
5 double/quad doors.
6
7 Door.owner is the master door.  If there is only one door, it points to itself.
8 If multiple doors, all will point to a single one.
9
10 Door.enemy chains from the master door through all doors linked in the chain.
11
12 */
13
14
15 /*
16 =============================================================================
17
18 THINK FUNCTIONS
19
20 =============================================================================
21 */
22
23 void() door_go_down;
24 void() door_go_up;
25 void() door_rotating_go_down;
26 void() door_rotating_go_up;
27
28 void door_blocked()
29 {SELFPARAM();
30         if((self.spawnflags & 8)
31 #ifdef SVQC
32                 && (other.takedamage != DAMAGE_NO)
33 #elif defined(CSQC)
34                 && !IS_DEAD(other)
35 #endif
36         )
37         { // KIll Kill Kill!!
38 #ifdef SVQC
39                 Damage (other, self, self, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
40 #endif
41         }
42         else
43         {
44 #ifdef SVQC
45                 if((self.dmg) && (other.takedamage == DAMAGE_YES))    // Shall we bite?
46                         Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
47 #endif
48
49                  // don't change direction for dead or dying stuff
50                 if(IS_DEAD(other)
51 #ifdef SVQC
52                         && (other.takedamage == DAMAGE_NO)
53 #endif
54                 )
55                 {
56                         if (self.wait >= 0)
57                         {
58                                 if (self.state == STATE_DOWN)
59                         if (self.classname == "door")
60                         {
61                                 door_go_up ();
62                         } else
63                         {
64                                 door_rotating_go_up ();
65                         }
66                                 else
67                         if (self.classname == "door")
68                         {
69                                 door_go_down ();
70                         } else
71                         {
72                                 door_rotating_go_down ();
73                         }
74                         }
75                 }
76 #ifdef SVQC
77                 else
78                 {
79                         //gib dying stuff just to make sure
80                         if((self.dmg) && (other.takedamage != DAMAGE_NO))    // Shall we bite?
81                                 Damage (other, self, self, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
82                 }
83 #endif
84         }
85 }
86
87 void door_hit_top()
88 {SELFPARAM();
89         if (self.noise1 != "")
90                 _sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
91         self.state = STATE_TOP;
92         if (self.spawnflags & DOOR_TOGGLE)
93                 return;         // don't come down automatically
94         if (self.classname == "door")
95         {
96                 self.SUB_THINK = door_go_down;
97         } else
98         {
99                 self.SUB_THINK = door_rotating_go_down;
100         }
101         self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
102 }
103
104 void door_hit_bottom()
105 {SELFPARAM();
106         if (self.noise1 != "")
107                 _sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
108         self.state = STATE_BOTTOM;
109 }
110
111 void door_go_down()
112 {SELFPARAM();
113         if (self.noise2 != "")
114                 _sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
115         if (self.max_health)
116         {
117                 self.takedamage = DAMAGE_YES;
118                 self.health = self.max_health;
119         }
120
121         self.state = STATE_DOWN;
122         SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, door_hit_bottom);
123 }
124
125 void door_go_up()
126 {SELFPARAM();
127         if (self.state == STATE_UP)
128                 return;         // already going up
129
130         if (self.state == STATE_TOP)
131         {       // reset top wait time
132                 self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
133                 return;
134         }
135
136         if (self.noise2 != "")
137                 _sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
138         self.state = STATE_UP;
139         SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, door_hit_top);
140
141         string oldmessage;
142         oldmessage = self.message;
143         self.message = "";
144         SUB_UseTargets(self, NULL, NULL);
145         self.message = oldmessage;
146 }
147
148
149 /*
150 =============================================================================
151
152 ACTIVATION FUNCTIONS
153
154 =============================================================================
155 */
156
157 bool door_check_keys(entity door, entity player)
158 {
159         if(door.owner)
160                 door = door.owner;
161
162         // no key needed
163         if(!door.itemkeys)
164                 return true;
165
166         // this door require a key
167         // only a player can have a key
168         if(!IS_PLAYER(player))
169                 return false;
170
171         int valid = (door.itemkeys & player.itemkeys);
172         door.itemkeys &= ~valid; // only some of the needed keys were given
173
174         if(!door.itemkeys)
175         {
176 #ifdef SVQC
177                 play2(player, SND(TALK));
178                 Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_DOOR_UNLOCKED);
179 #endif
180                 return true;
181         }
182
183         if(!valid)
184         {
185 #ifdef SVQC
186                 if(player.key_door_messagetime <= time)
187                 {
188                         play2(player, door.noise3);
189                         Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(door.itemkeys));
190                         player.key_door_messagetime = time + 2;
191                 }
192 #endif
193                 return false;
194         }
195
196         // door needs keys the player doesn't have
197 #ifdef SVQC
198         if(player.key_door_messagetime <= time)
199         {
200                 play2(player, door.noise3);
201                 Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(door.itemkeys));
202                 player.key_door_messagetime = time + 2;
203         }
204 #endif
205
206         return false;
207 }
208
209 void door_fire(entity this, entity actor, entity trigger)
210 {
211         entity starte;
212         if (this.owner != this)
213                 objerror ("door_fire: this.owner != this");
214
215         if (this.spawnflags & DOOR_TOGGLE)
216         {
217                 if (this.state == STATE_UP || this.state == STATE_TOP)
218                 {
219                         starte = self;
220                         do
221                         {
222                                 if (self.classname == "door")
223                                 {
224                                         door_go_down ();
225                                 }
226                                 else
227                                 {
228                                         door_rotating_go_down ();
229                                 }
230                                 setself(self.enemy);
231                         } while ( (self != starte) && (self != world) );
232                         setself(this);
233                         return;
234                 }
235         }
236
237 // trigger all paired doors
238         starte = self;
239         do
240         {
241                 if (self.classname == "door")
242                 {
243                         door_go_up ();
244                 } else
245                 {
246                         // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
247                         if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
248                         {
249                                 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
250                                 self.pos2 = '0 0 0' - self.pos2;
251                         }
252                         // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
253                         if (!((self.spawnflags & 2) &&  (self.spawnflags & 8) && self.state == STATE_DOWN
254                                 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
255                         {
256                                 door_rotating_go_up ();
257                         }
258                 }
259                 setself(self.enemy);
260         } while ( (self != starte) && (self != world) );
261         setself(this);
262 }
263
264 void door_use(entity this, entity actor, entity trigger)
265 {
266         //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
267
268         if (this.owner)
269                 WITHSELF(this.owner, door_fire(this.owner, actor, trigger));
270 }
271
272 void door_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
273 {
274         if(this.spawnflags & DOOR_NOSPLASH)
275                 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
276                         return;
277         this.health = this.health - damage;
278
279         if (this.itemkeys)
280         {
281                 // don't allow opening doors through damage if keys are required
282                 return;
283         }
284
285         if (this.health <= 0)
286         {
287                 this.owner.health = this.owner.max_health;
288                 this.owner.takedamage = DAMAGE_NO;      // wil be reset upon return
289                 door_use(this.owner, NULL, NULL);
290         }
291 }
292
293 .float door_finished;
294
295 /*
296 ================
297 door_touch
298
299 Prints messages
300 ================
301 */
302
303 void door_touch()
304 {SELFPARAM();
305         if (!IS_PLAYER(other))
306                 return;
307         if (self.owner.door_finished > time)
308                 return;
309
310         self.owner.door_finished = time + 2;
311
312 #ifdef SVQC
313         if (!(self.owner.dmg) && (self.owner.message != ""))
314         {
315                 if (IS_CLIENT(other))
316                         centerprint(other, self.owner.message);
317                 play2(other, self.owner.noise);
318         }
319 #endif
320 }
321
322 void door_generic_plat_blocked()
323 {SELFPARAM();
324
325         if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
326 #ifdef SVQC
327                 Damage (other, self, self, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
328 #endif
329         }
330         else
331         {
332
333 #ifdef SVQC
334                 if((self.dmg) && (other.takedamage == DAMAGE_YES))    // Shall we bite?
335                         Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
336 #endif
337
338                  //Dont chamge direction for dead or dying stuff
339                 if(IS_DEAD(other) && (other.takedamage == DAMAGE_NO))
340                 {
341                         if (self.wait >= 0)
342                         {
343                                 if (self.state == STATE_DOWN)
344                                         door_rotating_go_up ();
345                                 else
346                                         door_rotating_go_down ();
347                         }
348                 }
349 #ifdef SVQC
350                 else
351                 {
352                         //gib dying stuff just to make sure
353                         if((self.dmg) && (other.takedamage != DAMAGE_NO))    // Shall we bite?
354                                 Damage (other, self, self, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
355                 }
356 #endif
357         }
358 }
359
360 void door_rotating_hit_top()
361 {SELFPARAM();
362         if (self.noise1 != "")
363                 _sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
364         self.state = STATE_TOP;
365         if (self.spawnflags & DOOR_TOGGLE)
366                 return;         // don't come down automatically
367         self.SUB_THINK = door_rotating_go_down;
368         self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
369 }
370
371 void door_rotating_hit_bottom()
372 {SELFPARAM();
373         if (self.noise1 != "")
374                 _sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
375         if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
376         {
377                 self.pos2 = '0 0 0' - self.pos2;
378                 self.lip = 0;
379         }
380         self.state = STATE_BOTTOM;
381 }
382
383 void door_rotating_go_down()
384 {SELFPARAM();
385         if (self.noise2 != "")
386                 _sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
387         if (self.max_health)
388         {
389                 self.takedamage = DAMAGE_YES;
390                 self.health = self.max_health;
391         }
392
393         self.state = STATE_DOWN;
394         SUB_CalcAngleMove (self.pos1, TSPEED_LINEAR, self.speed, door_rotating_hit_bottom);
395 }
396
397 void door_rotating_go_up()
398 {SELFPARAM();
399         if (self.state == STATE_UP)
400                 return;         // already going up
401
402         if (self.state == STATE_TOP)
403         {       // reset top wait time
404                 self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
405                 return;
406         }
407         if (self.noise2 != "")
408                 _sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
409         self.state = STATE_UP;
410         SUB_CalcAngleMove (self.pos2, TSPEED_LINEAR, self.speed, door_rotating_hit_top);
411
412         string oldmessage;
413         oldmessage = self.message;
414         self.message = "";
415         SUB_UseTargets(self, NULL, other); // TODO: is other needed here?
416         self.message = oldmessage;
417 }
418
419
420 /*
421 =========================================
422 door trigger
423
424 Spawned if a door lacks a real activator
425 =========================================
426 */
427
428 void door_trigger_touch()
429 {SELFPARAM();
430         if (other.health < 1)
431 #ifdef SVQC
432                 if (!((other.iscreature || (other.flags & FL_PROJECTILE)) && !IS_DEAD(other)))
433 #elif defined(CSQC)
434                 if(!((IS_CLIENT(other) || other.classname == "csqcprojectile") && !IS_DEAD(other)))
435 #endif
436                         return;
437
438         if (time < self.door_finished)
439                 return;
440
441         // check if door is locked
442         if (!door_check_keys(self, other))
443                 return;
444
445         self.door_finished = time + 1;
446
447         door_use(this.owner, other, NULL);
448 }
449
450 void door_spawnfield(vector fmins, vector fmaxs)
451 {SELFPARAM();
452         entity  trigger;
453         vector  t1 = fmins, t2 = fmaxs;
454
455         trigger = new(doortriggerfield);
456         trigger.movetype = MOVETYPE_NONE;
457         trigger.solid = SOLID_TRIGGER;
458         trigger.owner = self;
459 #ifdef SVQC
460         trigger.touch = door_trigger_touch;
461 #elif defined(CSQC)
462         trigger.trigger_touch = door_trigger_touch;
463         trigger.draw = trigger_draw_generic;
464         trigger.drawmask = MASK_NORMAL;
465 #endif
466
467         setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
468 }
469
470
471 /*
472 =============
473 LinkDoors
474
475
476 =============
477 */
478
479 entity LinkDoors_nextent(entity cur, entity near, entity pass)
480 {SELFPARAM();
481         while((cur = find(cur, classname, self.classname)) && ((cur.spawnflags & 4) || cur.enemy))
482         {
483         }
484         return cur;
485 }
486
487 bool LinkDoors_isconnected(entity e1, entity e2, entity pass)
488 {
489         float DELTA = 4;
490         if((e1.absmin_x > e2.absmax_x + DELTA)
491         || (e1.absmin_y > e2.absmax_y + DELTA)
492         || (e1.absmin_z > e2.absmax_z + DELTA)
493         || (e2.absmin_x > e1.absmax_x + DELTA)
494         || (e2.absmin_y > e1.absmax_y + DELTA)
495         || (e2.absmin_z > e1.absmax_z + DELTA)
496         ) { return false; }
497         return true;
498 }
499
500 #ifdef SVQC
501 void door_link();
502 #endif
503 void LinkDoors(entity this)
504 {
505         entity  t;
506         vector  cmins, cmaxs;
507
508 #ifdef SVQC
509         door_link();
510 #endif
511
512         if (self.enemy)
513                 return;         // already linked by another door
514         if (self.spawnflags & 4)
515         {
516                 self.owner = self.enemy = self;
517
518                 if (self.health)
519                         return;
520                 IFTARGETED
521                         return;
522                 if (self.items)
523                         return;
524
525                 door_spawnfield(self.absmin, self.absmax);
526
527                 return;         // don't want to link this door
528         }
529
530         FindConnectedComponent(self, enemy, LinkDoors_nextent, LinkDoors_isconnected, world);
531
532         // set owner, and make a loop of the chain
533         LOG_TRACE("LinkDoors: linking doors:");
534         for(t = self; ; t = t.enemy)
535         {
536                 LOG_TRACE(" ", etos(t));
537                 t.owner = self;
538                 if(t.enemy == world)
539                 {
540                         t.enemy = self;
541                         break;
542                 }
543         }
544         LOG_TRACE("\n");
545
546         // collect health, targetname, message, size
547         cmins = self.absmin;
548         cmaxs = self.absmax;
549         for(t = self; ; t = t.enemy)
550         {
551                 if(t.health && !self.health)
552                         self.health = t.health;
553                 if((t.targetname != "") && (self.targetname == ""))
554                         self.targetname = t.targetname;
555                 if((t.message != "") && (self.message == ""))
556                         self.message = t.message;
557                 if (t.absmin_x < cmins_x)
558                         cmins_x = t.absmin_x;
559                 if (t.absmin_y < cmins_y)
560                         cmins_y = t.absmin_y;
561                 if (t.absmin_z < cmins_z)
562                         cmins_z = t.absmin_z;
563                 if (t.absmax_x > cmaxs_x)
564                         cmaxs_x = t.absmax_x;
565                 if (t.absmax_y > cmaxs_y)
566                         cmaxs_y = t.absmax_y;
567                 if (t.absmax_z > cmaxs_z)
568                         cmaxs_z = t.absmax_z;
569                 if(t.enemy == self)
570                         break;
571         }
572
573         // distribute health, targetname, message
574         for(t = self; t; t = t.enemy)
575         {
576                 t.health = self.health;
577                 t.targetname = self.targetname;
578                 t.message = self.message;
579                 if(t.enemy == self)
580                         break;
581         }
582
583         // shootable, or triggered doors just needed the owner/enemy links,
584         // they don't spawn a field
585
586         if (self.health)
587                 return;
588         IFTARGETED
589                 return;
590         if (self.items)
591                 return;
592
593         door_spawnfield(cmins, cmaxs);
594 }
595
596 REGISTER_NET_LINKED(ENT_CLIENT_DOOR)
597
598 #ifdef SVQC
599 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
600 if two doors touch, they are assumed to be connected and operate as a unit.
601
602 TOGGLE causes the door to wait in both the start and end states for a trigger event.
603
604 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).
605
606 GOLD_KEY causes the door to open only if the activator holds a gold key.
607
608 SILVER_KEY causes the door to open only if the activator holds a silver key.
609
610 "message"       is printed when the door is touched if it is a trigger door and it hasn't been fired yet
611 "angle"         determines the opening direction
612 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
613 "health"        if set, door must be shot open
614 "speed"         movement speed (100 default)
615 "wait"          wait before returning (3 default, -1 = never return)
616 "lip"           lip remaining at end of move (8 default)
617 "dmg"           damage to inflict when blocked (2 default)
618 "sounds"
619 0)      no sound
620 1)      stone
621 2)      base
622 3)      stone chain
623 4)      screechy metal
624 FIXME: only one sound set available at the time being
625
626 */
627
628 float door_send(entity to, float sf)
629 {SELFPARAM();
630         WriteHeader(MSG_ENTITY, ENT_CLIENT_DOOR);
631         WriteByte(MSG_ENTITY, sf);
632
633         if(sf & SF_TRIGGER_INIT)
634         {
635                 WriteString(MSG_ENTITY, self.classname);
636                 WriteByte(MSG_ENTITY, self.spawnflags);
637
638                 WriteString(MSG_ENTITY, self.model);
639
640                 trigger_common_write(self, true);
641
642                 WriteCoord(MSG_ENTITY, self.pos1_x);
643                 WriteCoord(MSG_ENTITY, self.pos1_y);
644                 WriteCoord(MSG_ENTITY, self.pos1_z);
645                 WriteCoord(MSG_ENTITY, self.pos2_x);
646                 WriteCoord(MSG_ENTITY, self.pos2_y);
647                 WriteCoord(MSG_ENTITY, self.pos2_z);
648
649                 WriteCoord(MSG_ENTITY, self.size_x);
650                 WriteCoord(MSG_ENTITY, self.size_y);
651                 WriteCoord(MSG_ENTITY, self.size_z);
652
653                 WriteShort(MSG_ENTITY, self.wait);
654                 WriteShort(MSG_ENTITY, self.speed);
655                 WriteByte(MSG_ENTITY, self.lip);
656                 WriteByte(MSG_ENTITY, self.state);
657                 WriteCoord(MSG_ENTITY, self.SUB_LTIME);
658         }
659
660         if(sf & SF_TRIGGER_RESET)
661         {
662                 // client makes use of this, we do not
663         }
664
665         if(sf & SF_TRIGGER_UPDATE)
666         {
667                 WriteCoord(MSG_ENTITY, self.origin_x);
668                 WriteCoord(MSG_ENTITY, self.origin_y);
669                 WriteCoord(MSG_ENTITY, self.origin_z);
670
671                 WriteCoord(MSG_ENTITY, self.pos1_x);
672                 WriteCoord(MSG_ENTITY, self.pos1_y);
673                 WriteCoord(MSG_ENTITY, self.pos1_z);
674                 WriteCoord(MSG_ENTITY, self.pos2_x);
675                 WriteCoord(MSG_ENTITY, self.pos2_y);
676                 WriteCoord(MSG_ENTITY, self.pos2_z);
677         }
678
679         return true;
680 }
681
682 void door_link()
683 {
684         // set size now, as everything is loaded
685         //FixSize(self);
686         //Net_LinkEntity(self, false, 0, door_send);
687 }
688 #endif
689
690 void door_init_startopen(entity this)
691 {
692         SUB_SETORIGIN(self, self.pos2);
693         self.pos2 = self.pos1;
694         self.pos1 = self.origin;
695
696 #ifdef SVQC
697         self.SendFlags |= SF_TRIGGER_UPDATE;
698 #endif
699 }
700
701 void door_reset(entity this)
702 {
703         SUB_SETORIGIN(this, this.pos1);
704         this.SUB_VELOCITY = '0 0 0';
705         this.state = STATE_BOTTOM;
706         this.SUB_THINK = func_null;
707         this.SUB_NEXTTHINK = 0;
708
709 #ifdef SVQC
710         this.SendFlags |= SF_TRIGGER_RESET;
711 #endif
712 }
713
714 #ifdef SVQC
715
716 // spawnflags require key (for now only func_door)
717 spawnfunc(func_door)
718 {
719         // Quake 1 keys compatibility
720         if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
721                 self.itemkeys |= ITEM_KEY_BIT(0);
722         if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
723                 self.itemkeys |= ITEM_KEY_BIT(1);
724
725         SetMovedir(self);
726
727         self.max_health = self.health;
728         if (!InitMovingBrushTrigger())
729                 return;
730         self.effects |= EF_LOWPRECISION;
731         self.classname = "door";
732
733         if(self.noise == "")
734                 self.noise = "misc/talk.wav";
735         if(self.noise3 == "")
736                 self.noise3 = "misc/talk.wav";
737         precache_sound(self.noise);
738         precache_sound(self.noise3);
739
740         self.blocked = door_blocked;
741         self.use = door_use;
742
743         if(self.dmg && (self.message == ""))
744                 self.message = "was squished";
745         if(self.dmg && (self.message2 == ""))
746                 self.message2 = "was squished by";
747
748         if (self.sounds > 0)
749         {
750                 precache_sound ("plats/medplat1.wav");
751                 precache_sound ("plats/medplat2.wav");
752                 self.noise2 = "plats/medplat1.wav";
753                 self.noise1 = "plats/medplat2.wav";
754         }
755
756         if (!self.speed)
757                 self.speed = 100;
758         if (!self.wait)
759                 self.wait = 3;
760         if (!self.lip)
761                 self.lip = 8;
762
763         self.pos1 = self.SUB_ORIGIN;
764         self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
765
766         if(self.spawnflags & DOOR_NONSOLID)
767                 self.solid = SOLID_NOT;
768
769 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
770 // but spawn in the open position
771         if (self.spawnflags & DOOR_START_OPEN)
772                 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
773
774         self.state = STATE_BOTTOM;
775
776         if (self.health)
777         {
778                 self.takedamage = DAMAGE_YES;
779                 self.event_damage = door_damage;
780         }
781
782         if (self.items)
783                 self.wait = -1;
784
785         self.touch = door_touch;
786
787 // LinkDoors can't be done until all of the doors have been spawned, so
788 // the sizes can be detected properly.
789         InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
790
791         self.reset = door_reset;
792 }
793
794 #elif defined(CSQC)
795
796 void door_draw(entity this)
797 {
798         Movetype_Physics_NoMatchServer(this);
799
800         trigger_draw_generic(this);
801 }
802
803 NET_HANDLE(ENT_CLIENT_DOOR, bool isnew)
804 {
805         float sf = ReadByte();
806
807         if(sf & SF_TRIGGER_INIT)
808         {
809                 this.classname = strzone(ReadString());
810                 this.spawnflags = ReadByte();
811
812                 this.mdl = strzone(ReadString());
813                 _setmodel(this, this.mdl);
814
815                 trigger_common_read(true);
816
817                 vector v;
818
819                 v.x = ReadCoord();
820                 v.y = ReadCoord();
821                 v.z = ReadCoord();
822                 this.pos1 = v;
823
824                 v.x = ReadCoord();
825                 v.y = ReadCoord();
826                 v.z = ReadCoord();
827                 this.pos2 = v;
828
829                 v.x = ReadCoord();
830                 v.y = ReadCoord();
831                 v.z = ReadCoord();
832                 this.size = v;
833
834                 this.wait = ReadShort();
835                 this.speed = ReadShort();
836                 this.lip = ReadByte();
837                 this.state = ReadByte();
838                 this.SUB_LTIME = ReadCoord();
839
840                 this.solid = SOLID_BSP;
841                 this.movetype = MOVETYPE_PUSH;
842                 this.trigger_touch = door_touch;
843                 this.draw = door_draw;
844                 this.drawmask = MASK_NORMAL;
845                 this.use = door_use;
846
847                 LinkDoors(this);
848
849                 if(this.spawnflags & DOOR_START_OPEN)
850                         door_init_startopen(this);
851
852                 this.move_time = time;
853                 this.move_origin = this.origin;
854                 this.move_movetype = MOVETYPE_PUSH;
855                 this.move_angles = this.angles;
856                 this.move_blocked = door_blocked;
857         }
858
859         if(sf & SF_TRIGGER_RESET)
860         {
861                 door_reset(this);
862         }
863
864         if(sf & SF_TRIGGER_UPDATE)
865         {
866                 this.origin_x = ReadCoord();
867                 this.origin_y = ReadCoord();
868                 this.origin_z = ReadCoord();
869                 setorigin(this, this.origin);
870                 this.move_origin = this.origin;
871
872                 this.pos1_x = ReadCoord();
873                 this.pos1_y = ReadCoord();
874                 this.pos1_z = ReadCoord();
875                 this.pos2_x = ReadCoord();
876                 this.pos2_y = ReadCoord();
877                 this.pos2_z = ReadCoord();
878         }
879         return true;
880 }
881
882 #endif