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