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