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