]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/triggers/func/door.qc
Merge branch 'master' into Mario/hagar_notfixed
[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();
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                 WITHSELF(self.owner, door_fire());
272         }
273 }
274
275 void door_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
276 {
277         if(this.spawnflags & DOOR_NOSPLASH)
278                 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
279                         return;
280         this.health = this.health - damage;
281
282         if (this.itemkeys)
283         {
284                 // don't allow opening doors through damage if keys are required
285                 return;
286         }
287
288         if (this.health <= 0)
289         {
290                 this.owner.health = this.owner.max_health;
291                 this.owner.takedamage = DAMAGE_NO;      // wil be reset upon return
292                 WITHSELF(this.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(IS_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)) && !IS_DEAD(other)))
436 #elif defined(CSQC)
437                 if(!((IS_CLIENT(other) || other.classname == "csqcprojectile") && !IS_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 = new(doortriggerfield);
462         trigger.movetype = MOVETYPE_NONE;
463         trigger.solid = SOLID_TRIGGER;
464         trigger.owner = self;
465 #ifdef SVQC
466         trigger.touch = door_trigger_touch;
467 #elif defined(CSQC)
468         trigger.trigger_touch = door_trigger_touch;
469         trigger.draw = trigger_draw_generic;
470         trigger.drawmask = MASK_NORMAL;
471 #endif
472
473         setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
474 }
475
476
477 /*
478 =============
479 LinkDoors
480
481
482 =============
483 */
484
485 entity LinkDoors_nextent(entity cur, entity near, entity pass)
486 {SELFPARAM();
487         while((cur = find(cur, classname, self.classname)) && ((cur.spawnflags & 4) || cur.enemy))
488         {
489         }
490         return cur;
491 }
492
493 bool LinkDoors_isconnected(entity e1, entity e2, entity pass)
494 {
495         float DELTA = 4;
496         if((e1.absmin_x > e2.absmax_x + DELTA)
497         || (e1.absmin_y > e2.absmax_y + DELTA)
498         || (e1.absmin_z > e2.absmax_z + DELTA)
499         || (e2.absmin_x > e1.absmax_x + DELTA)
500         || (e2.absmin_y > e1.absmax_y + DELTA)
501         || (e2.absmin_z > e1.absmax_z + DELTA)
502         ) { return false; }
503         return true;
504 }
505
506 #ifdef SVQC
507 void door_link();
508 #endif
509 void LinkDoors(entity this)
510 {
511         entity  t;
512         vector  cmins, cmaxs;
513
514 #ifdef SVQC
515         door_link();
516 #endif
517
518         if (self.enemy)
519                 return;         // already linked by another door
520         if (self.spawnflags & 4)
521         {
522                 self.owner = self.enemy = self;
523
524                 if (self.health)
525                         return;
526                 IFTARGETED
527                         return;
528                 if (self.items)
529                         return;
530
531                 door_spawnfield(self.absmin, self.absmax);
532
533                 return;         // don't want to link this door
534         }
535
536         FindConnectedComponent(self, enemy, LinkDoors_nextent, LinkDoors_isconnected, world);
537
538         // set owner, and make a loop of the chain
539         LOG_TRACE("LinkDoors: linking doors:");
540         for(t = self; ; t = t.enemy)
541         {
542                 LOG_TRACE(" ", etos(t));
543                 t.owner = self;
544                 if(t.enemy == world)
545                 {
546                         t.enemy = self;
547                         break;
548                 }
549         }
550         LOG_TRACE("\n");
551
552         // collect health, targetname, message, size
553         cmins = self.absmin;
554         cmaxs = self.absmax;
555         for(t = self; ; t = t.enemy)
556         {
557                 if(t.health && !self.health)
558                         self.health = t.health;
559                 if((t.targetname != "") && (self.targetname == ""))
560                         self.targetname = t.targetname;
561                 if((t.message != "") && (self.message == ""))
562                         self.message = t.message;
563                 if (t.absmin_x < cmins_x)
564                         cmins_x = t.absmin_x;
565                 if (t.absmin_y < cmins_y)
566                         cmins_y = t.absmin_y;
567                 if (t.absmin_z < cmins_z)
568                         cmins_z = t.absmin_z;
569                 if (t.absmax_x > cmaxs_x)
570                         cmaxs_x = t.absmax_x;
571                 if (t.absmax_y > cmaxs_y)
572                         cmaxs_y = t.absmax_y;
573                 if (t.absmax_z > cmaxs_z)
574                         cmaxs_z = t.absmax_z;
575                 if(t.enemy == self)
576                         break;
577         }
578
579         // distribute health, targetname, message
580         for(t = self; t; t = t.enemy)
581         {
582                 t.health = self.health;
583                 t.targetname = self.targetname;
584                 t.message = self.message;
585                 if(t.enemy == self)
586                         break;
587         }
588
589         // shootable, or triggered doors just needed the owner/enemy links,
590         // they don't spawn a field
591
592         if (self.health)
593                 return;
594         IFTARGETED
595                 return;
596         if (self.items)
597                 return;
598
599         door_spawnfield(cmins, cmaxs);
600 }
601
602 REGISTER_NET_LINKED(ENT_CLIENT_DOOR)
603
604 #ifdef SVQC
605 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
606 if two doors touch, they are assumed to be connected and operate as a unit.
607
608 TOGGLE causes the door to wait in both the start and end states for a trigger event.
609
610 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).
611
612 GOLD_KEY causes the door to open only if the activator holds a gold key.
613
614 SILVER_KEY causes the door to open only if the activator holds a silver key.
615
616 "message"       is printed when the door is touched if it is a trigger door and it hasn't been fired yet
617 "angle"         determines the opening direction
618 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
619 "health"        if set, door must be shot open
620 "speed"         movement speed (100 default)
621 "wait"          wait before returning (3 default, -1 = never return)
622 "lip"           lip remaining at end of move (8 default)
623 "dmg"           damage to inflict when blocked (2 default)
624 "sounds"
625 0)      no sound
626 1)      stone
627 2)      base
628 3)      stone chain
629 4)      screechy metal
630 FIXME: only one sound set available at the time being
631
632 */
633
634 float door_send(entity to, float sf)
635 {SELFPARAM();
636         WriteHeader(MSG_ENTITY, ENT_CLIENT_DOOR);
637         WriteByte(MSG_ENTITY, sf);
638
639         if(sf & SF_TRIGGER_INIT)
640         {
641                 WriteString(MSG_ENTITY, self.classname);
642                 WriteByte(MSG_ENTITY, self.spawnflags);
643
644                 WriteString(MSG_ENTITY, self.model);
645
646                 trigger_common_write(self, true);
647
648                 WriteCoord(MSG_ENTITY, self.pos1_x);
649                 WriteCoord(MSG_ENTITY, self.pos1_y);
650                 WriteCoord(MSG_ENTITY, self.pos1_z);
651                 WriteCoord(MSG_ENTITY, self.pos2_x);
652                 WriteCoord(MSG_ENTITY, self.pos2_y);
653                 WriteCoord(MSG_ENTITY, self.pos2_z);
654
655                 WriteCoord(MSG_ENTITY, self.size_x);
656                 WriteCoord(MSG_ENTITY, self.size_y);
657                 WriteCoord(MSG_ENTITY, self.size_z);
658
659                 WriteShort(MSG_ENTITY, self.wait);
660                 WriteShort(MSG_ENTITY, self.speed);
661                 WriteByte(MSG_ENTITY, self.lip);
662                 WriteByte(MSG_ENTITY, self.state);
663                 WriteCoord(MSG_ENTITY, self.SUB_LTIME);
664         }
665
666         if(sf & SF_TRIGGER_RESET)
667         {
668                 // client makes use of this, we do not
669         }
670
671         if(sf & SF_TRIGGER_UPDATE)
672         {
673                 WriteCoord(MSG_ENTITY, self.origin_x);
674                 WriteCoord(MSG_ENTITY, self.origin_y);
675                 WriteCoord(MSG_ENTITY, self.origin_z);
676
677                 WriteCoord(MSG_ENTITY, self.pos1_x);
678                 WriteCoord(MSG_ENTITY, self.pos1_y);
679                 WriteCoord(MSG_ENTITY, self.pos1_z);
680                 WriteCoord(MSG_ENTITY, self.pos2_x);
681                 WriteCoord(MSG_ENTITY, self.pos2_y);
682                 WriteCoord(MSG_ENTITY, self.pos2_z);
683         }
684
685         return true;
686 }
687
688 void door_link()
689 {
690         // set size now, as everything is loaded
691         //FixSize(self);
692         //Net_LinkEntity(self, false, 0, door_send);
693 }
694 #endif
695
696 void door_init_startopen(entity this)
697 {
698         SUB_SETORIGIN(self, self.pos2);
699         self.pos2 = self.pos1;
700         self.pos1 = self.origin;
701
702 #ifdef SVQC
703         self.SendFlags |= SF_TRIGGER_UPDATE;
704 #endif
705 }
706
707 void door_reset(entity this)
708 {
709         SUB_SETORIGIN(this, this.pos1);
710         this.SUB_VELOCITY = '0 0 0';
711         this.state = STATE_BOTTOM;
712         this.SUB_THINK = func_null;
713         this.SUB_NEXTTHINK = 0;
714
715 #ifdef SVQC
716         this.SendFlags |= SF_TRIGGER_RESET;
717 #endif
718 }
719
720 #ifdef SVQC
721
722 // spawnflags require key (for now only func_door)
723 spawnfunc(func_door)
724 {
725         // Quake 1 keys compatibility
726         if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
727                 self.itemkeys |= ITEM_KEY_BIT(0);
728         if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
729                 self.itemkeys |= ITEM_KEY_BIT(1);
730
731         SetMovedir(self);
732
733         self.max_health = self.health;
734         if (!InitMovingBrushTrigger())
735                 return;
736         self.effects |= EF_LOWPRECISION;
737         self.classname = "door";
738
739         if(self.noise == "")
740                 self.noise = "misc/talk.wav";
741         if(self.noise3 == "")
742                 self.noise3 = "misc/talk.wav";
743         precache_sound(self.noise);
744         precache_sound(self.noise3);
745
746         self.blocked = door_blocked;
747         self.use = door_use;
748
749         if(self.dmg && (self.message == ""))
750                 self.message = "was squished";
751         if(self.dmg && (self.message2 == ""))
752                 self.message2 = "was squished by";
753
754         if (self.sounds > 0)
755         {
756                 precache_sound ("plats/medplat1.wav");
757                 precache_sound ("plats/medplat2.wav");
758                 self.noise2 = "plats/medplat1.wav";
759                 self.noise1 = "plats/medplat2.wav";
760         }
761
762         if (!self.speed)
763                 self.speed = 100;
764         if (!self.wait)
765                 self.wait = 3;
766         if (!self.lip)
767                 self.lip = 8;
768
769         self.pos1 = self.SUB_ORIGIN;
770         self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
771
772         if(self.spawnflags & DOOR_NONSOLID)
773                 self.solid = SOLID_NOT;
774
775 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
776 // but spawn in the open position
777         if (self.spawnflags & DOOR_START_OPEN)
778                 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
779
780         self.state = STATE_BOTTOM;
781
782         if (self.health)
783         {
784                 self.takedamage = DAMAGE_YES;
785                 self.event_damage = door_damage;
786         }
787
788         if (self.items)
789                 self.wait = -1;
790
791         self.touch = door_touch;
792
793 // LinkDoors can't be done until all of the doors have been spawned, so
794 // the sizes can be detected properly.
795         InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
796
797         self.reset = door_reset;
798 }
799
800 #elif defined(CSQC)
801
802 void door_draw(entity this)
803 {
804         Movetype_Physics_NoMatchServer(this);
805
806         trigger_draw_generic(this);
807 }
808
809 NET_HANDLE(ENT_CLIENT_DOOR, bool isnew)
810 {
811         float sf = ReadByte();
812
813         if(sf & SF_TRIGGER_INIT)
814         {
815                 this.classname = strzone(ReadString());
816                 this.spawnflags = ReadByte();
817
818                 this.mdl = strzone(ReadString());
819                 _setmodel(this, this.mdl);
820
821                 trigger_common_read(true);
822
823                 vector v;
824
825                 v.x = ReadCoord();
826                 v.y = ReadCoord();
827                 v.z = ReadCoord();
828                 this.pos1 = v;
829
830                 v.x = ReadCoord();
831                 v.y = ReadCoord();
832                 v.z = ReadCoord();
833                 this.pos2 = v;
834
835                 v.x = ReadCoord();
836                 v.y = ReadCoord();
837                 v.z = ReadCoord();
838                 this.size = v;
839
840                 this.wait = ReadShort();
841                 this.speed = ReadShort();
842                 this.lip = ReadByte();
843                 this.state = ReadByte();
844                 this.SUB_LTIME = ReadCoord();
845
846                 this.solid = SOLID_BSP;
847                 this.movetype = MOVETYPE_PUSH;
848                 this.trigger_touch = door_touch;
849                 this.draw = door_draw;
850                 this.drawmask = MASK_NORMAL;
851                 this.use = door_use;
852
853                 LinkDoors(this);
854
855                 if(this.spawnflags & DOOR_START_OPEN)
856                         door_init_startopen(this);
857
858                 this.move_time = time;
859                 this.move_origin = this.origin;
860                 this.move_movetype = MOVETYPE_PUSH;
861                 this.move_angles = this.angles;
862                 this.move_blocked = door_blocked;
863         }
864
865         if(sf & SF_TRIGGER_RESET)
866         {
867                 door_reset(this);
868         }
869
870         if(sf & SF_TRIGGER_UPDATE)
871         {
872                 this.origin_x = ReadCoord();
873                 this.origin_y = ReadCoord();
874                 this.origin_z = ReadCoord();
875                 setorigin(this, this.origin);
876                 this.move_origin = this.origin;
877
878                 this.pos1_x = ReadCoord();
879                 this.pos1_y = ReadCoord();
880                 this.pos1_z = ReadCoord();
881                 this.pos2_x = ReadCoord();
882                 this.pos2_y = ReadCoord();
883                 this.pos2_z = ReadCoord();
884         }
885         return true;
886 }
887
888 #endif