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