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