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