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