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