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