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