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