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