home *** CD-ROM | disk | FTP | other *** search
/ Gambler 19 / GAMBLERCD19.BIN / UTILS / 3D / BRONIE / DUAL_LAU.ZIP / src / g_func.c < prev    next >
C/C++ Source or Header  |  1998-01-13  |  52KB  |  2,019 lines

  1. #include "g_local.h"
  2.  
  3. /*
  4. =========================================================
  5.  
  6.   PLATS
  7.  
  8.   movement options:
  9.  
  10.   linear
  11.   smooth start, hard stop
  12.   smooth start, smooth stop
  13.  
  14.   start
  15.   end
  16.   acceleration
  17.   speed
  18.   deceleration
  19.   begin sound
  20.   end sound
  21.   target fired when reaching end
  22.   wait at end
  23.  
  24.   object characteristics that use move segments
  25.   ---------------------------------------------
  26.   movetype_push, or movetype_stop
  27.   action when touched
  28.   action when blocked
  29.   action when used
  30.     disabled?
  31.   auto trigger spawning
  32.  
  33.  
  34. =========================================================
  35. */
  36.  
  37. #define PLAT_LOW_TRIGGER    1
  38.  
  39. #define    STATE_TOP            0
  40. #define    STATE_BOTTOM        1
  41. #define STATE_UP            2
  42. #define STATE_DOWN            3
  43.  
  44. #define DOOR_START_OPEN        1
  45. #define DOOR_REVERSE        2
  46. #define DOOR_CRUSHER        4
  47. #define DOOR_NOMONSTER        8
  48. #define DOOR_TOGGLE            32
  49. #define DOOR_X_AXIS            64
  50. #define DOOR_Y_AXIS            128
  51.  
  52.  
  53. //
  54. // Support routines for movement (changes in origin using velocity)
  55. //
  56.  
  57. void Move_Done (edict_t *ent)
  58. {
  59.     VectorClear (ent->velocity);
  60.     ent->moveinfo.endfunc (ent);
  61. }
  62.  
  63. void Move_Final (edict_t *ent)
  64. {
  65.     if (ent->moveinfo.remaining_distance == 0)
  66.     {
  67.         Move_Done (ent);
  68.         return;
  69.     }
  70.  
  71.     VectorScale (ent->moveinfo.dir, ent->moveinfo.remaining_distance / FRAMETIME, ent->velocity);
  72.  
  73.     ent->think = Move_Done;
  74.     ent->nextthink = level.time + FRAMETIME;
  75. }
  76.  
  77. void Move_Begin (edict_t *ent)
  78. {
  79.     float    frames;
  80.  
  81.     if ((ent->moveinfo.speed * FRAMETIME) >= ent->moveinfo.remaining_distance)
  82.     {
  83.         Move_Final (ent);
  84.         return;
  85.     }
  86.     VectorScale (ent->moveinfo.dir, ent->moveinfo.speed, ent->velocity);
  87.     frames = floor((ent->moveinfo.remaining_distance / ent->moveinfo.speed) / FRAMETIME);
  88.     ent->moveinfo.remaining_distance -= frames * ent->moveinfo.speed * FRAMETIME;
  89.     ent->nextthink = level.time + (frames * FRAMETIME);
  90.     ent->think = Move_Final;
  91. }
  92.  
  93. void Think_AccelMove (edict_t *ent);
  94.  
  95. void Move_Calc (edict_t *ent, vec3_t dest, void(*func)(edict_t*))
  96. {
  97.     VectorClear (ent->velocity);
  98.     VectorSubtract (dest, ent->s.origin, ent->moveinfo.dir);
  99.     ent->moveinfo.remaining_distance = VectorNormalize (ent->moveinfo.dir);
  100.     ent->moveinfo.endfunc = func;
  101.  
  102.     if (ent->moveinfo.speed == ent->moveinfo.accel && ent->moveinfo.speed == ent->moveinfo.decel)
  103.     {
  104.         if (level.current_entity == ((ent->flags & FL_TEAMSLAVE) ? ent->teammaster : ent))
  105.         {
  106.             Move_Begin (ent);
  107.         }
  108.         else
  109.         {
  110.             ent->nextthink = level.time + FRAMETIME;
  111.             ent->think = Move_Begin;
  112.         }
  113.     }
  114.     else
  115.     {
  116.         // accelerative
  117.         ent->moveinfo.current_speed = 0;
  118.         ent->think = Think_AccelMove;
  119.         ent->nextthink = level.time + FRAMETIME;
  120.     }
  121. }
  122.  
  123.  
  124. //
  125. // Support routines for angular movement (changes in angle using avelocity)
  126. //
  127.  
  128. void AngleMove_Done (edict_t *ent)
  129. {
  130.     VectorClear (ent->avelocity);
  131.     ent->moveinfo.endfunc (ent);
  132. }
  133.  
  134. void AngleMove_Final (edict_t *ent)
  135. {
  136.     vec3_t    move;
  137.  
  138.     if (ent->moveinfo.state == STATE_UP)
  139.         VectorSubtract (ent->moveinfo.end_angles, ent->s.angles, move);
  140.     else
  141.         VectorSubtract (ent->moveinfo.start_angles, ent->s.angles, move);
  142.  
  143.     if (VectorCompare (move, vec3_origin))
  144.     {
  145.         AngleMove_Done (ent);
  146.         return;
  147.     }
  148.  
  149.     VectorScale (move, 1.0/FRAMETIME, ent->avelocity);
  150.  
  151.     ent->think = AngleMove_Done;
  152.     ent->nextthink = level.time + FRAMETIME;
  153. }
  154.  
  155. void AngleMove_Begin (edict_t *ent)
  156. {
  157.     vec3_t    destdelta;
  158.     float    len;
  159.     float    traveltime;
  160.     float    frames;
  161.  
  162.     // set destdelta to the vector needed to move
  163.     if (ent->moveinfo.state == STATE_UP)
  164.         VectorSubtract (ent->moveinfo.end_angles, ent->s.angles, destdelta);
  165.     else
  166.         VectorSubtract (ent->moveinfo.start_angles, ent->s.angles, destdelta);
  167.     
  168.     // calculate length of vector
  169.     len = VectorLength (destdelta);
  170.     
  171.     // divide by speed to get time to reach dest
  172.     traveltime = len / ent->moveinfo.speed;
  173.  
  174.     if (traveltime < FRAMETIME)
  175.     {
  176.         AngleMove_Final (ent);
  177.         return;
  178.     }
  179.  
  180.     frames = floor(traveltime / FRAMETIME);
  181.  
  182.     // scale the destdelta vector by the time spent traveling to get velocity
  183.     VectorScale (destdelta, 1.0 / traveltime, ent->avelocity);
  184.  
  185.     // set nextthink to trigger a think when dest is reached
  186.     ent->nextthink = level.time + frames * FRAMETIME;
  187.     ent->think = AngleMove_Final;
  188. }
  189.  
  190. void AngleMove_Calc (edict_t *ent, void(*func)(edict_t*))
  191. {
  192.     VectorClear (ent->avelocity);
  193.     ent->moveinfo.endfunc = func;
  194.     if (level.current_entity == ((ent->flags & FL_TEAMSLAVE) ? ent->teammaster : ent))
  195.     {
  196.         AngleMove_Begin (ent);
  197.     }
  198.     else
  199.     {
  200.         ent->nextthink = level.time + FRAMETIME;
  201.         ent->think = AngleMove_Begin;
  202.     }
  203. }
  204.  
  205.  
  206. /*
  207. ==============
  208. Think_AccelMove
  209.  
  210. The team has completed a frame of movement, so
  211. change the speed for the next frame
  212. ==============
  213. */
  214. #define AccelerationDistance(target, rate)    (target * ((target / rate) + 1) / 2)
  215.  
  216. void plat_CalcAcceleratedMove(moveinfo_t *moveinfo)
  217. {
  218.     float    accel_dist;
  219.     float    decel_dist;
  220.  
  221.     moveinfo->move_speed = moveinfo->speed;
  222.  
  223.     if (moveinfo->remaining_distance < moveinfo->accel)
  224.     {
  225.         moveinfo->current_speed = moveinfo->remaining_distance;
  226.         return;
  227.     }
  228.  
  229.     accel_dist = AccelerationDistance (moveinfo->speed, moveinfo->accel);
  230.     decel_dist = AccelerationDistance (moveinfo->speed, moveinfo->decel);
  231.  
  232.     if ((moveinfo->remaining_distance - accel_dist - decel_dist) < 0)
  233.     {
  234.         float    f;
  235.  
  236.         f = (moveinfo->accel + moveinfo->decel) / (moveinfo->accel * moveinfo->decel);
  237.         moveinfo->move_speed = (-2 + sqrt(4 - 4 * f * (-2 * moveinfo->remaining_distance))) / (2 * f);
  238.         decel_dist = AccelerationDistance (moveinfo->move_speed, moveinfo->decel);
  239.     }
  240.  
  241.     moveinfo->decel_distance = decel_dist;
  242. }
  243.  
  244. void plat_Accelerate (moveinfo_t *moveinfo)
  245. {
  246.     // are we decelerating?
  247.     if (moveinfo->remaining_distance <= moveinfo->decel_distance)
  248.     {
  249.         if (moveinfo->remaining_distance < moveinfo->decel_distance)
  250.         {
  251.             if (moveinfo->next_speed)
  252.             {
  253.                 moveinfo->current_speed = moveinfo->next_speed;
  254.                 moveinfo->next_speed = 0;
  255.                 return;
  256.             }
  257.             if (moveinfo->current_speed > moveinfo->decel)
  258.                 moveinfo->current_speed -= moveinfo->decel;
  259.         }
  260.         return;
  261.     }
  262.  
  263.     // are we at full speed and need to start decelerating during this move?
  264.     if (moveinfo->current_speed == moveinfo->move_speed)
  265.         if ((moveinfo->remaining_distance - moveinfo->current_speed) < moveinfo->decel_distance)
  266.         {
  267.             float    p1_distance;
  268.             float    p2_distance;
  269.             float    distance;
  270.  
  271.             p1_distance = moveinfo->remaining_distance - moveinfo->decel_distance;
  272.             p2_distance = moveinfo->move_speed * (1.0 - (p1_distance / moveinfo->move_speed));
  273.             distance = p1_distance + p2_distance;
  274.             moveinfo->current_speed = moveinfo->move_speed;
  275.             moveinfo->next_speed = moveinfo->move_speed - moveinfo->decel * (p2_distance / distance);
  276.             return;
  277.         }
  278.  
  279.     // are we accelerating?
  280.     if (moveinfo->current_speed < moveinfo->speed)
  281.     {
  282.         float    old_speed;
  283.         float    p1_distance;
  284.         float    p1_speed;
  285.         float    p2_distance;
  286.         float    distance;
  287.  
  288.         old_speed = moveinfo->current_speed;
  289.  
  290.         // figure simple acceleration up to move_speed
  291.         moveinfo->current_speed += moveinfo->accel;
  292.         if (moveinfo->current_speed > moveinfo->speed)
  293.             moveinfo->current_speed = moveinfo->speed;
  294.  
  295.         // are we accelerating throughout this entire move?
  296.         if ((moveinfo->remaining_distance - moveinfo->current_speed) >= moveinfo->decel_distance)
  297.             return;
  298.  
  299.         // during this move we will accelrate from current_speed to move_speed
  300.         // and cross over the decel_distance; figure the average speed for the
  301.         // entire move
  302.         p1_distance = moveinfo->remaining_distance - moveinfo->decel_distance;
  303.         p1_speed = (old_speed + moveinfo->move_speed) / 2.0;
  304.         p2_distance = moveinfo->move_speed * (1.0 - (p1_distance / p1_speed));
  305.         distance = p1_distance + p2_distance;
  306.         moveinfo->current_speed = (p1_speed * (p1_distance / distance)) + (moveinfo->move_speed * (p2_distance / distance));
  307.         moveinfo->next_speed = moveinfo->move_speed - moveinfo->decel * (p2_distance / distance);
  308.         return;
  309.     }
  310.  
  311.     // we are at constant velocity (move_speed)
  312.     return;
  313. }
  314.  
  315. void Think_AccelMove (edict_t *ent)
  316. {
  317.     ent->moveinfo.remaining_distance -= ent->moveinfo.current_speed;
  318.  
  319.     if (ent->moveinfo.current_speed == 0)        // starting or blocked
  320.         plat_CalcAcceleratedMove(&ent->moveinfo);
  321.  
  322.     plat_Accelerate (&ent->moveinfo);
  323.  
  324.     // will the entire move complete on next frame?
  325.     if (ent->moveinfo.remaining_distance <= ent->moveinfo.current_speed)
  326.     {
  327.         Move_Final (ent);
  328.         return;
  329.     }
  330.  
  331.     VectorScale (ent->moveinfo.dir, ent->moveinfo.current_speed*10, ent->velocity);
  332.     ent->nextthink = level.time + FRAMETIME;
  333.     ent->think = Think_AccelMove;
  334. }
  335.  
  336.  
  337. void plat_go_down (edict_t *ent);
  338.  
  339. void plat_hit_top (edict_t *ent)
  340. {
  341.     if (!(ent->flags & FL_TEAMSLAVE))
  342.     {
  343.         if (ent->moveinfo.sound_end)
  344.             gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_end, 1, ATTN_STATIC, 0);
  345.         ent->s.sound = 0;
  346.     }
  347.     ent->moveinfo.state = STATE_TOP;
  348.  
  349.     ent->think = plat_go_down;
  350.     ent->nextthink = level.time + 3;
  351. }
  352.  
  353. void plat_hit_bottom (edict_t *ent)
  354. {
  355.     if (!(ent->flags & FL_TEAMSLAVE))
  356.     {
  357.         if (ent->moveinfo.sound_end)
  358.             gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_end, 1, ATTN_STATIC, 0);
  359.         ent->s.sound = 0;
  360.     }
  361.     ent->moveinfo.state = STATE_BOTTOM;
  362. }
  363.  
  364. void plat_go_down (edict_t *ent)
  365. {
  366.     if (!(ent->flags & FL_TEAMSLAVE))
  367.     {
  368.         if (ent->moveinfo.sound_start)
  369.             gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_start, 1, ATTN_STATIC, 0);
  370.         ent->s.sound = ent->moveinfo.sound_middle;
  371.     }
  372.     ent->moveinfo.state = STATE_DOWN;
  373.     Move_Calc (ent, ent->moveinfo.end_origin, plat_hit_bottom);
  374. }
  375.  
  376. void plat_go_up (edict_t *ent)
  377. {
  378.     if (!(ent->flags & FL_TEAMSLAVE))
  379.     {
  380.         if (ent->moveinfo.sound_start)
  381.             gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_start, 1, ATTN_STATIC, 0);
  382.         ent->s.sound = ent->moveinfo.sound_middle;
  383.     }
  384.     ent->moveinfo.state = STATE_UP;
  385.     Move_Calc (ent, ent->moveinfo.start_origin, plat_hit_top);
  386. }
  387.  
  388. void plat_blocked (edict_t *self, edict_t *other)
  389. {
  390.     if (!(other->svflags & SVF_MONSTER) && (!other->client))
  391.     {
  392.         // give it a chance to go away on it's own terms (like gibs)
  393.         T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0);
  394.         // if it's still there, nuke it
  395.         BecomeExplosion1 (other);
  396.         return;
  397.     }
  398.  
  399.     T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0);
  400.  
  401.     if (self->moveinfo.state == STATE_UP)
  402.         plat_go_down (self);
  403.     else if (self->moveinfo.state == STATE_DOWN)
  404.         plat_go_up (self);
  405. }
  406.  
  407.  
  408. void Use_Plat (edict_t *ent, edict_t *other, edict_t *activator)
  409.     if (ent->think)
  410.         return;        // already down
  411.     plat_go_down (ent);
  412. }
  413.  
  414.  
  415. void Touch_Plat_Center (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  416. {
  417.     if (!other->client)
  418.         return;
  419.         
  420.     if (other->health <= 0)
  421.         return;
  422.  
  423.     ent = ent->enemy;    // now point at the plat, not the trigger
  424.     if (ent->moveinfo.state == STATE_BOTTOM)
  425.         plat_go_up (ent);
  426.     else if (ent->moveinfo.state == STATE_TOP)
  427.         ent->nextthink = level.time + 1;    // the player is still on the plat, so delay going down
  428. }
  429.  
  430. void plat_spawn_inside_trigger (edict_t *ent)
  431. {
  432.     edict_t    *trigger;
  433.     vec3_t    tmin, tmax;
  434.  
  435. //
  436. // middle trigger
  437. //    
  438.     trigger = G_Spawn();
  439.     trigger->touch = Touch_Plat_Center;
  440.     trigger->movetype = MOVETYPE_NONE;
  441.     trigger->solid = SOLID_TRIGGER;
  442.     trigger->enemy = ent;
  443.     
  444.     tmin[0] = ent->mins[0] + 25;
  445.     tmin[1] = ent->mins[1] + 25;
  446.     tmin[2] = ent->mins[2];
  447.  
  448.     tmax[0] = ent->maxs[0] - 25;
  449.     tmax[1] = ent->maxs[1] - 25;
  450.     tmax[2] = ent->maxs[2] + 8;
  451.  
  452.     tmin[2] = tmax[2] - (ent->pos1[2] - ent->pos2[2] + st.lip);
  453.  
  454.     if (ent->spawnflags & PLAT_LOW_TRIGGER)
  455.         tmax[2] = tmin[2] + 8;
  456.     
  457.     if (tmax[0] - tmin[0] <= 0)
  458.     {
  459.         tmin[0] = (ent->mins[0] + ent->maxs[0]) *0.5;
  460.         tmax[0] = tmin[0] + 1;
  461.     }
  462.     if (tmax[1] - tmin[1] <= 0)
  463.     {
  464.         tmin[1] = (ent->mins[1] + ent->maxs[1]) *0.5;
  465.         tmax[1] = tmin[1] + 1;
  466.     }
  467.     
  468.     VectorCopy (tmin, trigger->mins);
  469.     VectorCopy (tmax, trigger->maxs);
  470.  
  471.     gi.linkentity (trigger);
  472. }
  473.  
  474.  
  475. /*QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER
  476. speed    default 150
  477.  
  478. Plats are always drawn in the extended position, so they will light correctly.
  479.  
  480. If the plat is the target of another trigger or button, it will start out disabled in the extended position until it is trigger, when it will lower and become a normal plat.
  481.  
  482. "speed"    overrides default 200.
  483. "accel" overrides default 500
  484. "lip"    overrides default 8 pixel lip
  485.  
  486. If the "height" key is set, that will determine the amount the plat moves, instead of being implicitly determoveinfoned by the model's height.
  487.  
  488. Set "sounds" to one of the following:
  489. 1) base fast
  490. 2) chain slow
  491. */
  492. void SP_func_plat (edict_t *ent)
  493. {
  494.     VectorClear (ent->s.angles);
  495.     ent->solid = SOLID_BSP;
  496.     ent->movetype = MOVETYPE_PUSH;
  497.  
  498.     gi.setmodel (ent, ent->model);
  499.  
  500.     ent->blocked = plat_blocked;
  501.  
  502.     if (!ent->speed)
  503.         ent->speed = 20;
  504.     else
  505.         ent->speed *= 0.1;
  506.  
  507.     if (!ent->accel)
  508.         ent->accel = 5;
  509.     else
  510.         ent->accel *= 0.1;
  511.  
  512.     if (!ent->decel)
  513.         ent->decel = 5;
  514.     else
  515.         ent->decel *= 0.1;
  516.  
  517.     if (!ent->dmg)
  518.         ent->dmg = 2;
  519.  
  520.     if (!st.lip)
  521.         st.lip = 8;
  522.  
  523.     // pos1 is the top position, pos2 is the bottom
  524.     VectorCopy (ent->s.origin, ent->pos1);
  525.     VectorCopy (ent->s.origin, ent->pos2);
  526.     if (st.height)
  527.         ent->pos2[2] -= st.height;
  528.     else
  529.         ent->pos2[2] -= (ent->maxs[2] - ent->mins[2]) - st.lip;
  530.  
  531.     ent->use = Use_Plat;
  532.  
  533.     plat_spawn_inside_trigger (ent);    // the "start moving" trigger    
  534.  
  535.     if (ent->targetname)
  536.     {
  537.         ent->moveinfo.state = STATE_UP;
  538.     }
  539.     else
  540.     {
  541.         VectorCopy (ent->pos2, ent->s.origin);
  542.         gi.linkentity (ent);
  543.         ent->moveinfo.state = STATE_BOTTOM;
  544.     }
  545.  
  546.     ent->moveinfo.speed = ent->speed;
  547.     ent->moveinfo.accel = ent->accel;
  548.     ent->moveinfo.decel = ent->decel;
  549.     ent->moveinfo.wait = ent->wait;
  550.     VectorCopy (ent->pos1, ent->moveinfo.start_origin);
  551.     VectorCopy (ent->s.angles, ent->moveinfo.start_angles);
  552.     VectorCopy (ent->pos2, ent->moveinfo.end_origin);
  553.     VectorCopy (ent->s.angles, ent->moveinfo.end_angles);
  554.  
  555.     ent->moveinfo.sound_start = gi.soundindex ("plats/pt1_strt.wav");
  556.     ent->moveinfo.sound_middle = gi.soundindex ("plats/pt1_mid.wav");
  557.     ent->moveinfo.sound_end = gi.soundindex ("plats/pt1_end.wav");
  558. }
  559.  
  560. //====================================================================
  561.  
  562. /*QUAKED func_rotating (0 .5 .8) ? START_ON REVERSE X_AXIS Y_AXIS TOUCH_PAIN STOP ANIMATED ANIMATED_FAST
  563. You need to have an origin brush as part of this entity.  The center of that brush will be
  564. the point around which it is rotated. It will rotate around the Z axis by default.  You can
  565. check either the X_AXIS or Y_AXIS box to change that.
  566.  
  567. "speed" determines how fast it moves; default value is 100.
  568. "dmg"    damage to inflict when blocked (2 default)
  569.  
  570. REVERSE will cause the it to rotate in the opposite direction.
  571. STOP mean it will stop moving instead of pushing entities
  572. */
  573.  
  574. void rotating_blocked (edict_t *self, edict_t *other)
  575. {
  576.     T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0);
  577. }
  578.  
  579. void rotating_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  580. {
  581.     if (self->avelocity[0] || self->avelocity[1] || self->avelocity[2])
  582.         T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0);
  583. }
  584.  
  585. void rotating_use (edict_t *self, edict_t *other, edict_t *activator)
  586. {
  587.     if (!VectorCompare (self->avelocity, vec3_origin))
  588.     {
  589.         self->s.sound = 0;
  590.         VectorClear (self->avelocity);
  591.         self->touch = NULL;
  592.     }
  593.     else
  594.     {
  595.         self->s.sound = self->moveinfo.sound_middle;
  596.         VectorScale (self->movedir, self->speed, self->avelocity);
  597.         if (self->spawnflags & 16)
  598.             self->touch = rotating_touch;
  599.     }
  600. }
  601.  
  602. void SP_func_rotating (edict_t *ent)
  603. {
  604.     ent->solid = SOLID_BSP;
  605.     if (ent->spawnflags & 32)
  606.         ent->movetype = MOVETYPE_STOP;
  607.     else
  608.         ent->movetype = MOVETYPE_PUSH;
  609.  
  610.     // set the axis of rotation
  611.     VectorClear(ent->movedir);
  612.     if (ent->spawnflags & 4)
  613.         ent->movedir[2] = 1.0;
  614.     else if (ent->spawnflags & 8)
  615.         ent->movedir[0] = 1.0;
  616.     else // Z_AXIS
  617.         ent->movedir[1] = 1.0;
  618.  
  619.     // check for reverse rotation
  620.     if (ent->spawnflags & 2)
  621.         VectorNegate (ent->movedir, ent->movedir);
  622.  
  623.     if (!ent->speed)
  624.         ent->speed = 100;
  625.     if (!ent->dmg)
  626.         ent->dmg = 2;
  627.  
  628. //    ent->moveinfo.sound_middle = "doors/hydro1.wav";
  629.  
  630.     ent->use = rotating_use;
  631.     if (ent->dmg)
  632.         ent->blocked = rotating_blocked;
  633.  
  634.     if (ent->spawnflags & 1)
  635.         ent->use (ent, NULL, NULL);
  636.  
  637.     if (ent->spawnflags & 64)
  638.         ent->s.effects |= EF_ANIM_ALL;
  639.     if (ent->spawnflags & 128)
  640.         ent->s.effects |= EF_ANIM_ALLFAST;
  641.  
  642.     gi.setmodel (ent, ent->model);
  643.     gi.linkentity (ent);
  644. }
  645.  
  646. /*
  647. ======================================================================
  648.  
  649. BUTTONS
  650.  
  651. ======================================================================
  652. */
  653.  
  654. /*QUAKED func_button (0 .5 .8) ?
  655. When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again.
  656.  
  657. "angle"        determines the opening direction
  658. "target"    all entities with a matching targetname will be used
  659. "speed"        override the default 40 speed
  660. "wait"        override the default 1 second wait (-1 = never return)
  661. "lip"        override the default 4 pixel lip remaining at end of move
  662. "health"    if set, the button must be killed instead of touched
  663. "sounds"
  664. 1) silent
  665. 2) steam metal
  666. 3) wooden clunk
  667. 4) metallic click
  668. 5) in-out
  669. */
  670.  
  671. void button_done (edict_t *self)
  672. {
  673.     self->moveinfo.state = STATE_BOTTOM;
  674.     self->s.effects &= ~EF_ANIM23;
  675.     self->s.effects |= EF_ANIM01;
  676. }
  677.  
  678. void button_return (edict_t *self)
  679. {
  680.     self->moveinfo.state = STATE_DOWN;
  681.  
  682.     Move_Calc (self, self->moveinfo.start_origin, button_done);
  683.  
  684.     self->s.frame = 0;
  685.  
  686.     if (self->health)
  687.         self->takedamage = DAMAGE_YES;
  688. }
  689.  
  690. void button_wait (edict_t *self)
  691. {
  692.     self->moveinfo.state = STATE_TOP;
  693.     self->s.effects &= ~EF_ANIM01;
  694.     self->s.effects |= EF_ANIM23;
  695.  
  696.     G_UseTargets (self, self->activator);
  697.     self->s.frame = 1;
  698.     if (self->moveinfo.wait >= 0)
  699.     {
  700.         self->nextthink = level.time + self->moveinfo.wait;
  701.         self->think = button_return;
  702.     }
  703. }
  704.  
  705. void button_fire (edict_t *self)
  706. {
  707.     if (self->moveinfo.state == STATE_UP || self->moveinfo.state == STATE_TOP)
  708.         return;
  709.  
  710.     self->moveinfo.state = STATE_UP;
  711.     if (self->moveinfo.sound_start && !(self->flags & FL_TEAMSLAVE))
  712.         gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
  713.     Move_Calc (self, self->moveinfo.end_origin, button_wait);
  714. }
  715.  
  716. void button_use (edict_t *self, edict_t *other, edict_t *activator)
  717. {
  718.     self->activator = activator;
  719.     button_fire (self);
  720. }
  721.  
  722. void button_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  723. {
  724.     if (!other->client)
  725.         return;
  726.  
  727.     if (other->health <= 0)
  728.         return;
  729.  
  730.     self->activator = other;
  731.     button_fire (self);
  732. }
  733.  
  734. void button_killed (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  735. {
  736.     self->activator = attacker;
  737.     self->health = self->max_health;
  738.     self->takedamage = DAMAGE_NO;
  739.     button_fire (self);
  740. }
  741.  
  742. void SP_func_button (edict_t *ent)
  743. {
  744.     vec3_t    abs_movedir;
  745.     float    dist;
  746.  
  747.     G_SetMovedir (ent->s.angles, ent->movedir);
  748.     ent->movetype = MOVETYPE_STOP;
  749.     ent->solid = SOLID_BSP;
  750.     gi.setmodel (ent, ent->model);
  751.  
  752.     if (ent->sounds != 1)
  753.         ent->moveinfo.sound_start = gi.soundindex ("switches/butn2.wav");
  754.     
  755.     if (!ent->speed)
  756.         ent->speed = 40;
  757.     if (!ent->accel)
  758.         ent->accel = ent->speed;
  759.     if (!ent->decel)
  760.         ent->decel = ent->speed;
  761.  
  762.     if (!ent->wait)
  763.         ent->wait = 3;
  764.     if (!st.lip)
  765.         st.lip = 4;
  766.  
  767.     VectorCopy (ent->s.origin, ent->pos1);
  768.     abs_movedir[0] = fabs(ent->movedir[0]);
  769.     abs_movedir[1] = fabs(ent->movedir[1]);
  770.     abs_movedir[2] = fabs(ent->movedir[2]);
  771.     dist = abs_movedir[0] * ent->size[0] + abs_movedir[1] * ent->size[1] + abs_movedir[2] * ent->size[2] - st.lip;
  772.     VectorMA (ent->pos1, dist, ent->movedir, ent->pos2);
  773.  
  774.     ent->use = button_use;
  775.     ent->s.effects |= EF_ANIM01;
  776.  
  777.     if (ent->health)
  778.     {
  779.         ent->max_health = ent->health;
  780.         ent->die = button_killed;
  781.         ent->takedamage = DAMAGE_YES;
  782.     }
  783.     else if (! ent->targetname)
  784.         ent->touch = button_touch;
  785.  
  786.     ent->moveinfo.state = STATE_BOTTOM;
  787.  
  788.     ent->moveinfo.speed = ent->speed;
  789.     ent->moveinfo.accel = ent->accel;
  790.     ent->moveinfo.decel = ent->decel;
  791.     ent->moveinfo.wait = ent->wait;
  792.     VectorCopy (ent->pos1, ent->moveinfo.start_origin);
  793.     VectorCopy (ent->s.angles, ent->moveinfo.start_angles);
  794.     VectorCopy (ent->pos2, ent->moveinfo.end_origin);
  795.     VectorCopy (ent->s.angles, ent->moveinfo.end_angles);
  796.  
  797.     gi.linkentity (ent);
  798. }
  799.  
  800. /*
  801. ======================================================================
  802.  
  803. DOORS
  804.  
  805.   spawn a trigger surrounding the entire team unless it is
  806.   allready targeted by another
  807.  
  808. ======================================================================
  809. */
  810.  
  811. /*QUAKED func_door (0 .5 .8) ? START_OPEN x CRUSHER NOMONSTER ANIMATED TOGGLE ANIMATED_FAST
  812. TOGGLE        wait in both the start and end states for a trigger event.
  813. START_OPEN    the door to moves 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).
  814. NOMONSTER    monsters will not trigger this door
  815.  
  816. "message"    is printed when the door is touched if it is a trigger door and it hasn't been fired yet
  817. "angle"        determines the opening direction
  818. "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
  819. "health"    if set, door must be shot open
  820. "speed"        movement speed (100 default)
  821. "wait"        wait before returning (3 default, -1 = never return)
  822. "lip"        lip remaining at end of move (8 default)
  823. "dmg"        damage to inflict when blocked (2 default)
  824. "sounds"
  825. 1)    silent
  826. 2)    light
  827. 3)    medium
  828. 4)    heavy
  829. */
  830.  
  831. void door_use_areaportals (edict_t *self, qboolean open)
  832. {
  833.     edict_t    *t = NULL;
  834.  
  835.     if (!self->target)
  836.         return;
  837.  
  838.     while ((t = G_Find (t, FOFS(targetname), self->target)))
  839.     {
  840.         if (Q_stricmp(t->classname, "func_areaportal") == 0)
  841.         {
  842.             gi.SetAreaPortalState (t->style, open);
  843.         }
  844.     }
  845. }
  846.  
  847. void door_go_down (edict_t *self);
  848.  
  849. void door_hit_top (edict_t *self)
  850. {
  851.     if (!(self->flags & FL_TEAMSLAVE))
  852.     {
  853.         if (self->moveinfo.sound_end)
  854.             gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_end, 1, ATTN_STATIC, 0);
  855.         self->s.sound = 0;
  856.     }
  857.     self->moveinfo.state = STATE_TOP;
  858.     if (self->spawnflags & DOOR_TOGGLE)
  859.         return;
  860.     if (self->moveinfo.wait >= 0)
  861.     {
  862.         self->think = door_go_down;
  863.         self->nextthink = level.time + self->moveinfo.wait;
  864.     }
  865. }
  866.  
  867. void door_hit_bottom (edict_t *self)
  868. {
  869.     if (!(self->flags & FL_TEAMSLAVE))
  870.     {
  871.         if (self->moveinfo.sound_end)
  872.             gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_end, 1, ATTN_STATIC, 0);
  873.         self->s.sound = 0;
  874.     }
  875.     self->moveinfo.state = STATE_BOTTOM;
  876.     door_use_areaportals (self, false);
  877. }
  878.  
  879. void door_go_down (edict_t *self)
  880. {
  881.     if (!(self->flags & FL_TEAMSLAVE))
  882.     {
  883.         if (self->moveinfo.sound_start)
  884.             gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
  885.         self->s.sound = self->moveinfo.sound_middle;
  886.     }
  887.     if (self->max_health)
  888.     {
  889.         self->takedamage = DAMAGE_YES;
  890.         self->health = self->max_health;
  891.     }
  892.     
  893.     self->moveinfo.state = STATE_DOWN;
  894.     if (strcmp(self->classname, "func_door") == 0)
  895.         Move_Calc (self, self->moveinfo.start_origin, door_hit_bottom);
  896.     else if (strcmp(self->classname, "func_door_rotating") == 0)
  897.         AngleMove_Calc (self, door_hit_bottom);
  898. }
  899.  
  900. void door_go_up (edict_t *self, edict_t *activator)
  901. {
  902.     if (self->moveinfo.state == STATE_UP)
  903.         return;        // already going up
  904.  
  905.     if (self->moveinfo.state == STATE_TOP)
  906.     {    // reset top wait time
  907.         if (self->moveinfo.wait >= 0)
  908.             self->nextthink = level.time + self->moveinfo.wait;
  909.         return;
  910.     }
  911.     
  912.     if (!(self->flags & FL_TEAMSLAVE))
  913.     {
  914.         if (self->moveinfo.sound_start)
  915.             gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
  916.         self->s.sound = self->moveinfo.sound_middle;
  917.     }
  918.     self->moveinfo.state = STATE_UP;
  919.     if (strcmp(self->classname, "func_door") == 0)
  920.         Move_Calc (self, self->moveinfo.end_origin, door_hit_top);
  921.     else if (strcmp(self->classname, "func_door_rotating") == 0)
  922.         AngleMove_Calc (self, door_hit_top);
  923.  
  924.     G_UseTargets (self, activator);
  925.     door_use_areaportals (self, true);
  926. }
  927.  
  928. void door_use (edict_t *self, edict_t *other, edict_t *activator)
  929. {
  930.     edict_t    *ent;
  931.  
  932.     if (self->flags & FL_TEAMSLAVE)
  933.         return;
  934.  
  935.     if (self->spawnflags & DOOR_TOGGLE)
  936.     {
  937.         if (self->moveinfo.state == STATE_UP || self->moveinfo.state == STATE_TOP)
  938.         {
  939.             // trigger all paired doors
  940.             for (ent = self ; ent ; ent = ent->teamchain)
  941.             {
  942.                 ent->message = NULL;
  943.                 ent->touch = NULL;
  944.                 door_go_down (ent);
  945.             }
  946.             return;
  947.         }
  948.     }
  949.     
  950.     // trigger all paired doors
  951.     for (ent = self ; ent ; ent = ent->teamchain)
  952.     {
  953.         ent->message = NULL;
  954.         ent->touch = NULL;
  955.         door_go_up (ent, activator);
  956.     }
  957. }
  958.  
  959. void Touch_DoorTrigger (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  960. {
  961.     if (other->health <= 0)
  962.         return;
  963.  
  964.     if (!(other->svflags & SVF_MONSTER) && (!other->client))
  965.         return;
  966.  
  967.     if ((self->owner->spawnflags & DOOR_NOMONSTER) && (other->svflags & SVF_MONSTER))
  968.         return;
  969.  
  970.     if (level.time < self->touch_debounce_time)
  971.         return;
  972.     self->touch_debounce_time = level.time + 1.0;
  973.  
  974.     door_use (self->owner, other, other);
  975. }
  976.  
  977. void Think_CalcMoveSpeed (edict_t *self)
  978. {
  979.     edict_t    *ent;
  980.     float    min;
  981.     float    time;
  982.     float    newspeed;
  983.     float    ratio;
  984.     float    dist;
  985.  
  986.     if (self->flags & FL_TEAMSLAVE)
  987.         return;        // only the team master does this
  988.  
  989.     // find the smallest distance any member of the team will be moving
  990.     min = fabs(self->moveinfo.distance);
  991.     for (ent = self->teamchain; ent; ent = ent->teamchain)
  992.     {
  993.         dist = fabs(ent->moveinfo.distance);
  994.         if (dist < min)
  995.             min = dist;
  996.     }
  997.  
  998.     time = min / self->moveinfo.speed;
  999.  
  1000.     // adjust speeds so they will all complete at the same time
  1001.     for (ent = self; ent; ent = ent->teamchain)
  1002.     {
  1003.         newspeed = fabs(ent->moveinfo.distance) / time;
  1004.         ratio = newspeed / ent->moveinfo.speed;
  1005.         if (ent->moveinfo.accel == ent->moveinfo.speed)
  1006.             ent->moveinfo.accel = newspeed;
  1007.         else
  1008.             ent->moveinfo.accel *= ratio;
  1009.         if (ent->moveinfo.decel == ent->moveinfo.speed)
  1010.             ent->moveinfo.decel = newspeed;
  1011.         else
  1012.             ent->moveinfo.decel *= ratio;
  1013.         ent->moveinfo.speed = newspeed;
  1014.     }
  1015. }
  1016.  
  1017. void Think_SpawnDoorTrigger (edict_t *ent)
  1018. {
  1019.     edict_t        *other;
  1020.     vec3_t        mins, maxs;
  1021.  
  1022.     if (ent->flags & FL_TEAMSLAVE)
  1023.         return;        // only the team leader spawns a trigger
  1024.  
  1025.     VectorCopy (ent->absmin, mins);
  1026.     VectorCopy (ent->absmax, maxs);
  1027.  
  1028.     for (other = ent->teamchain ; other ; other=other->teamchain)
  1029.     {
  1030.         AddPointToBounds (other->absmin, mins, maxs);
  1031.         AddPointToBounds (other->absmax, mins, maxs);
  1032.     }
  1033.  
  1034.     // expand 
  1035.     mins[0] -= 60;
  1036.     mins[1] -= 60;
  1037.     maxs[0] += 60;
  1038.     maxs[1] += 60;
  1039.  
  1040.     other = G_Spawn ();
  1041.     VectorCopy (mins, other->mins);
  1042.     VectorCopy (maxs, other->maxs);
  1043.     other->owner = ent;
  1044.     other->solid = SOLID_TRIGGER;
  1045.     other->movetype = MOVETYPE_NONE;
  1046.     other->touch = Touch_DoorTrigger;
  1047.     gi.linkentity (other);
  1048.  
  1049.     if (ent->spawnflags & DOOR_START_OPEN)
  1050.         door_use_areaportals (ent, true);
  1051.  
  1052.     Think_CalcMoveSpeed (ent);
  1053. }
  1054.  
  1055. void door_blocked  (edict_t *self, edict_t *other)
  1056. {
  1057.     edict_t    *ent;
  1058.  
  1059.     if (!(other->svflags & SVF_MONSTER) && (!other->client))
  1060.     {
  1061.         // give it a chance to go away on it's own terms (like gibs)
  1062.         T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0);
  1063.         // if it's still there, nuke it
  1064.         BecomeExplosion1 (other);
  1065.         return;
  1066.     }
  1067.  
  1068.     T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0);
  1069.  
  1070.     if (self->spawnflags & DOOR_CRUSHER)
  1071.         return;
  1072.  
  1073.  
  1074. // if a door has a negative wait, it would never come back if blocked,
  1075. // so let it just squash the object to death real fast
  1076.     if (self->moveinfo.wait >= 0)
  1077.     {
  1078.         if (self->moveinfo.state == STATE_DOWN)
  1079.         {
  1080.             for (ent = self->teammaster ; ent ; ent = ent->teamchain)
  1081.                 door_go_up (ent, ent->activator);
  1082.         }
  1083.         else
  1084.         {
  1085.             for (ent = self->teammaster ; ent ; ent = ent->teamchain)
  1086.                 door_go_down (ent);
  1087.         }
  1088.     }
  1089. }
  1090.  
  1091. void door_killed (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  1092. {
  1093.     edict_t    *ent;
  1094.  
  1095.     for (ent = self->teammaster ; ent ; ent = ent->teamchain)
  1096.     {
  1097.         ent->health = ent->max_health;
  1098.         ent->takedamage = DAMAGE_NO;
  1099.     }
  1100.     door_use (self->teammaster, attacker, attacker);
  1101. }
  1102.  
  1103. void door_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  1104. {
  1105.     if (!other->client)
  1106.         return;
  1107.  
  1108.     if (level.time < self->touch_debounce_time)
  1109.         return;
  1110.     self->touch_debounce_time = level.time + 5.0;
  1111.  
  1112.     gi.centerprintf (other, "%s", self->message);
  1113.     gi.sound (other, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
  1114. }
  1115.  
  1116. void SP_func_door (edict_t *ent)
  1117. {
  1118.     vec3_t    abs_movedir;
  1119.  
  1120.     if (ent->sounds != 1)
  1121.     {
  1122.         ent->moveinfo.sound_start = gi.soundindex  ("doors/dr1_strt.wav");
  1123.         ent->moveinfo.sound_middle = gi.soundindex  ("doors/dr1_mid.wav");
  1124.         ent->moveinfo.sound_end = gi.soundindex  ("doors/dr1_end.wav");
  1125.     }
  1126.  
  1127.     G_SetMovedir (ent->s.angles, ent->movedir);
  1128.     ent->movetype = MOVETYPE_PUSH;
  1129.     ent->solid = SOLID_BSP;
  1130.     gi.setmodel (ent, ent->model);
  1131.  
  1132.     ent->blocked = door_blocked;
  1133.     ent->use = door_use;
  1134.     
  1135.     if (!ent->speed)
  1136.         ent->speed = 100;
  1137.     if (!ent->accel)
  1138.         ent->accel = ent->speed;
  1139.     if (!ent->decel)
  1140.         ent->decel = ent->speed;
  1141.  
  1142.     if (!ent->wait)
  1143.         ent->wait = 3;
  1144.     if (!st.lip)
  1145.         st.lip = 8;
  1146.     if (!ent->dmg)
  1147.         ent->dmg = 2;
  1148.  
  1149.     // calculate second position
  1150.     VectorCopy (ent->s.origin, ent->pos1);
  1151.     abs_movedir[0] = fabs(ent->movedir[0]);
  1152.     abs_movedir[1] = fabs(ent->movedir[1]);
  1153.     abs_movedir[2] = fabs(ent->movedir[2]);
  1154.     ent->moveinfo.distance = abs_movedir[0] * ent->size[0] + abs_movedir[1] * ent->size[1] + abs_movedir[2] * ent->size[2] - st.lip;
  1155.     VectorMA (ent->pos1, ent->moveinfo.distance, ent->movedir, ent->pos2);
  1156.  
  1157.     // if it starts open, switch the positions
  1158.     if (ent->spawnflags & DOOR_START_OPEN)
  1159.     {
  1160.         VectorCopy (ent->pos2, ent->s.origin);
  1161.         VectorCopy (ent->pos1, ent->pos2);
  1162.         VectorCopy (ent->s.origin, ent->pos1);
  1163.     }
  1164.  
  1165.     ent->moveinfo.state = STATE_BOTTOM;
  1166.  
  1167.     if (ent->health)
  1168.     {
  1169.         ent->takedamage = DAMAGE_YES;
  1170.         ent->die = door_killed;
  1171.         ent->max_health = ent->health;
  1172.     }
  1173.     else if (ent->targetname && ent->message)
  1174.     {
  1175.         gi.soundindex ("misc/talk.wav");
  1176.         ent->touch = door_touch;
  1177.     }
  1178.     
  1179.     ent->moveinfo.speed = ent->speed;
  1180.     ent->moveinfo.accel = ent->accel;
  1181.     ent->moveinfo.decel = ent->decel;
  1182.     ent->moveinfo.wait = ent->wait;
  1183.     VectorCopy (ent->pos1, ent->moveinfo.start_origin);
  1184.     VectorCopy (ent->s.angles, ent->moveinfo.start_angles);
  1185.     VectorCopy (ent->pos2, ent->moveinfo.end_origin);
  1186.     VectorCopy (ent->s.angles, ent->moveinfo.end_angles);
  1187.  
  1188.     if (ent->spawnflags & 16)
  1189.         ent->s.effects |= EF_ANIM_ALL;
  1190.     if (ent->spawnflags & 64)
  1191.         ent->s.effects |= EF_ANIM_ALLFAST;
  1192.  
  1193.     // to simplify logic elsewhere, make non-teamed doors into a team of one
  1194.     if (!ent->team)
  1195.         ent->teammaster = ent;
  1196.  
  1197.     gi.linkentity (ent);
  1198.  
  1199.     ent->nextthink = level.time + FRAMETIME;
  1200.     if (ent->health || ent->targetname)
  1201.         ent->think = Think_CalcMoveSpeed;
  1202.     else
  1203.         ent->think = Think_SpawnDoorTrigger;
  1204. }
  1205.  
  1206.  
  1207. /*QUAKED func_door_rotating (0 .5 .8) ? START_OPEN REVERSE CRUSHER NOMONSTER ANIMATED TOGGLE X_AXIS Y_AXIS
  1208. TOGGLE causes the door to wait in both the start and end states for a trigger event.
  1209.  
  1210. START_OPEN    the door to moves 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).
  1211. NOMONSTER    monsters will not trigger this door
  1212.  
  1213. You need to have an origin brush as part of this entity.  The center of that brush will be
  1214. the point around which it is rotated. It will rotate around the Z axis by default.  You can
  1215. check either the X_AXIS or Y_AXIS box to change that.
  1216.  
  1217. "distance" is how many degrees the door will be rotated.
  1218. "speed" determines how fast the door moves; default value is 100.
  1219.  
  1220. REVERSE will cause the door to rotate in the opposite direction.
  1221.  
  1222. "message"    is printed when the door is touched if it is a trigger door and it hasn't been fired yet
  1223. "angle"        determines the opening direction
  1224. "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
  1225. "health"    if set, door must be shot open
  1226. "speed"        movement speed (100 default)
  1227. "wait"        wait before returning (3 default, -1 = never return)
  1228. "dmg"        damage to inflict when blocked (2 default)
  1229. "sounds"
  1230. 1)    silent
  1231. 2)    light
  1232. 3)    medium
  1233. 4)    heavy
  1234. */
  1235.  
  1236. void SP_func_door_rotating (edict_t *ent)
  1237. {
  1238.     VectorClear (ent->s.angles);
  1239.  
  1240.     // set the axis of rotation
  1241.     VectorClear(ent->movedir);
  1242.     if (ent->spawnflags & DOOR_X_AXIS)
  1243.         ent->movedir[2] = 1.0;
  1244.     else if (ent->spawnflags & DOOR_Y_AXIS)
  1245.         ent->movedir[0] = 1.0;
  1246.     else // Z_AXIS
  1247.         ent->movedir[1] = 1.0;
  1248.  
  1249.     // check for reverse rotation
  1250.     if (ent->spawnflags & DOOR_REVERSE)
  1251.         VectorNegate (ent->movedir, ent->movedir);
  1252.  
  1253.     if (!st.distance)
  1254.     {
  1255.         gi.dprintf("%s at %s with no distance set\n", ent->classname, vtos(ent->s.origin));
  1256.         st.distance = 90;
  1257.     }
  1258.  
  1259.     VectorCopy (ent->s.angles, ent->pos1);
  1260.     VectorMA (ent->s.angles, st.distance, ent->movedir, ent->pos2);
  1261.     ent->moveinfo.distance = st.distance;
  1262.  
  1263.     ent->movetype = MOVETYPE_PUSH;
  1264.     ent->solid = SOLID_BSP;
  1265.     gi.setmodel (ent, ent->model);
  1266.  
  1267.     ent->blocked = door_blocked;
  1268.     ent->use = door_use;
  1269.  
  1270.     if (!ent->speed)
  1271.         ent->speed = 100;
  1272.     if (!ent->accel)
  1273.         ent->accel = ent->speed;
  1274.     if (!ent->decel)
  1275.         ent->decel = ent->speed;
  1276.  
  1277.     if (!ent->wait)
  1278.         ent->wait = 3;
  1279.     if (!ent->dmg)
  1280.         ent->dmg = 2;
  1281.  
  1282.     if (ent->sounds != 1)
  1283.     {
  1284.         ent->moveinfo.sound_start = gi.soundindex  ("doors/dr1_strt.wav");
  1285.         ent->moveinfo.sound_middle = gi.soundindex  ("doors/dr1_mid.wav");
  1286.         ent->moveinfo.sound_end = gi.soundindex  ("doors/dr1_end.wav");
  1287.     }
  1288.  
  1289.     // if it starts open, switch the positions
  1290.     if (ent->spawnflags & DOOR_START_OPEN)
  1291.     {
  1292.         VectorCopy (ent->pos2, ent->s.angles);
  1293.         VectorCopy (ent->pos1, ent->pos2);
  1294.         VectorCopy (ent->s.angles, ent->pos1);
  1295.         VectorNegate (ent->movedir, ent->movedir);
  1296.     }
  1297.  
  1298.     if (ent->health)
  1299.     {
  1300.         ent->takedamage = DAMAGE_YES;
  1301.         ent->die = door_killed;
  1302.         ent->max_health = ent->health;
  1303.     }
  1304.     
  1305.     if (ent->targetname && ent->message)
  1306.     {
  1307.         gi.soundindex ("misc/talk.wav");
  1308.         ent->touch = door_touch;
  1309.     }
  1310.  
  1311.     ent->moveinfo.state = STATE_BOTTOM;
  1312.     ent->moveinfo.speed = ent->speed;
  1313.     ent->moveinfo.accel = ent->accel;
  1314.     ent->moveinfo.decel = ent->decel;
  1315.     ent->moveinfo.wait = ent->wait;
  1316.     VectorCopy (ent->s.origin, ent->moveinfo.start_origin);
  1317.     VectorCopy (ent->pos1, ent->moveinfo.start_angles);
  1318.     VectorCopy (ent->s.origin, ent->moveinfo.end_origin);
  1319.     VectorCopy (ent->pos2, ent->moveinfo.end_angles);
  1320.  
  1321.     if (ent->spawnflags & 16)
  1322.         ent->s.effects |= EF_ANIM_ALL;
  1323.  
  1324.     gi.linkentity (ent);
  1325.  
  1326.     ent->nextthink = level.time + FRAMETIME;
  1327.     if (ent->health || ent->targetname)
  1328.         ent->think = Think_CalcMoveSpeed;
  1329.     else
  1330.         ent->think = Think_SpawnDoorTrigger;
  1331. }
  1332.  
  1333.  
  1334. /*QUAKED func_water (0 .5 .8) ? START_OPEN
  1335. func_water is a moveable water brush.  It must be targeted to operate.  Use a non-water texture at your own risk.
  1336.  
  1337. START_OPEN causes the water to move to its destination when spawned and operate in reverse.
  1338.  
  1339. "angle"        determines the opening direction (up or down only)
  1340. "speed"        movement speed (25 default)
  1341. "wait"        wait before returning (-1 default, -1 = TOGGLE)
  1342. "lip"        lip remaining at end of move (0 default)
  1343. "sounds"    (yes, these need to be changed)
  1344. 0)    no sound
  1345. 1)    water
  1346. 2)    lava
  1347. */
  1348.  
  1349. void SP_func_water (edict_t *self)
  1350. {
  1351.     vec3_t    abs_movedir;
  1352.  
  1353.     G_SetMovedir (self->s.angles, self->movedir);
  1354.     self->movetype = MOVETYPE_PUSH;
  1355.     self->solid = SOLID_BSP;
  1356.     gi.setmodel (self, self->model);
  1357.  
  1358.     switch (self->sounds)
  1359.     {
  1360.         default:
  1361.             break;
  1362.  
  1363.         case 1: // water
  1364.             self->moveinfo.sound_start = gi.soundindex  ("world/mov_watr.wav");
  1365.             self->moveinfo.sound_end = gi.soundindex  ("world/stp_watr.wav");
  1366.             break;
  1367.  
  1368.         case 2: // lava
  1369.             self->moveinfo.sound_start = gi.soundindex  ("world/mov_watr.wav");
  1370.             self->moveinfo.sound_end = gi.soundindex  ("world/stp_watr.wav");
  1371.             break;
  1372.     }
  1373.  
  1374.     // calculate second position
  1375.     VectorCopy (self->s.origin, self->pos1);
  1376.     abs_movedir[0] = fabs(self->movedir[0]);
  1377.     abs_movedir[1] = fabs(self->movedir[1]);
  1378.     abs_movedir[2] = fabs(self->movedir[2]);
  1379.     self->moveinfo.distance = abs_movedir[0] * self->size[0] + abs_movedir[1] * self->size[1] + abs_movedir[2] * self->size[2] - st.lip;
  1380.     VectorMA (self->pos1, self->moveinfo.distance, self->movedir, self->pos2);
  1381.  
  1382.     // if it starts open, switch the positions
  1383.     if (self->spawnflags & DOOR_START_OPEN)
  1384.     {
  1385.         VectorCopy (self->pos2, self->s.origin);
  1386.         VectorCopy (self->pos1, self->pos2);
  1387.         VectorCopy (self->s.origin, self->pos1);
  1388.     }
  1389.  
  1390.     VectorCopy (self->pos1, self->moveinfo.start_origin);
  1391.     VectorCopy (self->s.angles, self->moveinfo.start_angles);
  1392.     VectorCopy (self->pos2, self->moveinfo.end_origin);
  1393.     VectorCopy (self->s.angles, self->moveinfo.end_angles);
  1394.  
  1395.     self->moveinfo.state = STATE_BOTTOM;
  1396.  
  1397.     if (!self->speed)
  1398.         self->speed = 25;
  1399.     self->moveinfo.accel = self->moveinfo.decel = self->moveinfo.speed = self->speed;
  1400.  
  1401.     if (!self->wait)
  1402.         self->wait = -1;
  1403.     self->moveinfo.wait = self->wait;
  1404.  
  1405.     self->use = door_use;
  1406.  
  1407.     if (self->wait == -1)
  1408.         self->spawnflags |= DOOR_TOGGLE;
  1409.  
  1410.     self->classname = "func_door";
  1411.  
  1412.     gi.linkentity (self);
  1413. }
  1414.  
  1415.  
  1416. #define TRAIN_START_ON        1
  1417. #define TRAIN_TOGGLE        2
  1418. #define TRAIN_BLOCK_STOPS    4
  1419.  
  1420. /*QUAKED func_train (0 .5 .8) ? START_ON TOGGLE BLOCK_STOPS
  1421. Trains are moving platforms that players can ride.
  1422. The targets origin specifies the min point of the train at each corner.
  1423. The train spawns at the first target it is pointing at.
  1424. If the train is the target of a button or trigger, it will not begin moving until activated.
  1425. speed    default 100
  1426. dmg        default    2
  1427. noise    looping sound to play when the train is in motion
  1428.  
  1429. */
  1430. void train_next (edict_t *self);
  1431.  
  1432. void train_blocked (edict_t *self, edict_t *other)
  1433. {
  1434.     if (!(other->svflags & SVF_MONSTER) && (!other->client))
  1435.     {
  1436.         // give it a chance to go away on it's own terms (like gibs)
  1437.         T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0);
  1438.         // if it's still there, nuke it
  1439.         if (other)
  1440.             BecomeExplosion1 (other);
  1441.         return;
  1442.     }
  1443.  
  1444.     if (level.time < self->touch_debounce_time)
  1445.         return;
  1446.  
  1447.     if (!self->dmg)
  1448.         return;
  1449.     self->touch_debounce_time = level.time + 0.5;
  1450.     T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0);
  1451. }
  1452.  
  1453. void train_wait (edict_t *self)
  1454. {
  1455.     if (self->target_ent->pathtarget)
  1456.     {
  1457.         char    *savetarget;
  1458.         edict_t    *ent;
  1459.  
  1460.         ent = self->target_ent;
  1461.         savetarget = ent->target;
  1462.         ent->target = ent->pathtarget;
  1463.         G_UseTargets (ent, self->activator);
  1464.         ent->target = savetarget;
  1465.  
  1466.         // make sure we didn't get killed by a killtarget
  1467.         if (!self->inuse)
  1468.             return;
  1469.     }
  1470.  
  1471.     if (self->moveinfo.wait)
  1472.     {
  1473.         if (self->moveinfo.wait > 0)
  1474.         {
  1475.             self->nextthink = level.time + self->moveinfo.wait;
  1476.             self->think = train_next;
  1477.         }
  1478.         else if (self->spawnflags & TRAIN_TOGGLE)  // && wait < 0
  1479.         {
  1480.             train_next (self);
  1481.             self->spawnflags &= ~TRAIN_START_ON;
  1482.             VectorClear (self->velocity);
  1483.             self->nextthink = 0;
  1484.         }
  1485.  
  1486.         if (!(self->flags & FL_TEAMSLAVE))
  1487.         {
  1488.             if (self->moveinfo.sound_end)
  1489.                 gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_end, 1, ATTN_STATIC, 0);
  1490.             self->s.sound = 0;
  1491.         }
  1492.     }
  1493.     else
  1494.     {
  1495.         train_next (self);
  1496.     }
  1497.     
  1498. }
  1499.  
  1500. void train_next (edict_t *self)
  1501. {
  1502.     edict_t        *ent;
  1503.     vec3_t        dest;
  1504.     qboolean    first;
  1505.  
  1506.     first = true;
  1507. again:
  1508.     if (!self->target)
  1509.     {
  1510. //        gi.dprintf ("train_next: no next target\n");
  1511.         return;
  1512.     }
  1513.  
  1514.     ent = G_PickTarget (self->target);
  1515.     if (!ent)
  1516.     {
  1517.         gi.dprintf ("train_next: bad target %s\n", self->target);
  1518.         return;
  1519.     }
  1520.  
  1521.     self->target = ent->target;
  1522.  
  1523.     // check for a teleport path_corner
  1524.     if (ent->spawnflags & 1)
  1525.     {
  1526.         if (!first)
  1527.         {
  1528.             gi.dprintf ("connected teleport path_corners, see %s at %s\n", ent->classname, vtos(ent->s.origin));
  1529.             return;
  1530.         }
  1531.         first = false;
  1532.         VectorSubtract (ent->s.origin, self->mins, self->s.origin);
  1533.         VectorCopy (self->s.origin, self->s.old_origin);
  1534.         gi.linkentity (self);
  1535.         goto again;
  1536.     }
  1537.  
  1538.     self->moveinfo.wait = ent->wait;
  1539.     self->target_ent = ent;
  1540.  
  1541.     if (!(self->flags & FL_TEAMSLAVE))
  1542.     {
  1543.         if (self->moveinfo.sound_start)
  1544.             gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
  1545.         self->s.sound = self->moveinfo.sound_middle;
  1546.     }
  1547.  
  1548.     VectorSubtract (ent->s.origin, self->mins, dest);
  1549.     self->moveinfo.state = STATE_TOP;
  1550.     VectorCopy (self->s.origin, self->moveinfo.start_origin);
  1551.     VectorCopy (dest, self->moveinfo.end_origin);
  1552.     Move_Calc (self, dest, train_wait);
  1553.     self->spawnflags |= TRAIN_START_ON;
  1554. }
  1555.  
  1556. void train_resume (edict_t *self)
  1557. {
  1558.     edict_t    *ent;
  1559.     vec3_t    dest;
  1560.  
  1561.     ent = self->target_ent;
  1562.  
  1563.     VectorSubtract (ent->s.origin, self->mins, dest);
  1564.     self->moveinfo.state = STATE_TOP;
  1565.     VectorCopy (self->s.origin, self->moveinfo.start_origin);
  1566.     VectorCopy (dest, self->moveinfo.end_origin);
  1567.     Move_Calc (self, dest, train_wait);
  1568.     self->spawnflags |= TRAIN_START_ON;
  1569. }
  1570.  
  1571. void func_train_find (edict_t *self)
  1572. {
  1573.     edict_t *ent;
  1574.  
  1575.     if (!self->target)
  1576.     {
  1577.         gi.dprintf ("train_find: no target\n");
  1578.         return;
  1579.     }
  1580.     ent = G_PickTarget (self->target);
  1581.     if (!ent)
  1582.     {
  1583.         gi.dprintf ("train_find: target %s not found\n", self->target);
  1584.         return;
  1585.     }
  1586.     self->target = ent->target;
  1587.  
  1588.     VectorSubtract (ent->s.origin, self->mins, self->s.origin);
  1589.     gi.linkentity (self);
  1590.  
  1591.     // if not triggered, start immediately
  1592.     if (!self->targetname)
  1593.         self->spawnflags |= TRAIN_START_ON;
  1594.  
  1595.     if (self->spawnflags & TRAIN_START_ON)
  1596.     {
  1597.         self->nextthink = level.time + FRAMETIME;
  1598.         self->think = train_next;
  1599.         self->activator = self;
  1600.     }
  1601. }
  1602.  
  1603. void train_use (edict_t *self, edict_t *other, edict_t *activator)
  1604. {
  1605.     self->activator = activator;
  1606.  
  1607.     if (self->spawnflags & TRAIN_START_ON)
  1608.     {
  1609.         if (!(self->spawnflags & TRAIN_TOGGLE))
  1610.             return;
  1611.         self->spawnflags &= ~TRAIN_START_ON;
  1612.         VectorClear (self->velocity);
  1613.         self->nextthink = 0;
  1614.     }
  1615.     else
  1616.     {
  1617.         if (self->target_ent)
  1618.             train_resume(self);
  1619.         else
  1620.             train_next(self);
  1621.     }
  1622. }
  1623.  
  1624. void SP_func_train (edict_t *self)
  1625. {
  1626.     self->movetype = MOVETYPE_PUSH;
  1627.  
  1628.     VectorClear (self->s.angles);
  1629.     self->blocked = train_blocked;
  1630.     if (self->spawnflags & TRAIN_BLOCK_STOPS)
  1631.         self->dmg = 0;
  1632.     else
  1633.     {
  1634.         if (!self->dmg)
  1635.             self->dmg = 100;
  1636.     }
  1637.     self->solid = SOLID_BSP;
  1638.     gi.setmodel (self, self->model);
  1639.  
  1640.     if (st.noise)
  1641.         self->moveinfo.sound_middle = gi.soundindex  (st.noise);
  1642.  
  1643.     if (!self->speed)
  1644.         self->speed = 100;
  1645.  
  1646.     self->moveinfo.speed = self->speed;
  1647.     self->moveinfo.accel = self->moveinfo.decel = self->moveinfo.speed;
  1648.  
  1649.     self->use = train_use;
  1650.  
  1651.     gi.linkentity (self);
  1652.  
  1653.     if (self->target)
  1654.     {
  1655.         // start trains on the second frame, to make sure their targets have had
  1656.         // a chance to spawn
  1657.         self->nextthink = level.time + FRAMETIME;
  1658.         self->think = func_train_find;
  1659.     }
  1660.     else
  1661.     {
  1662.         gi.dprintf ("func_train without a target at %s\n", vtos(self->absmin));
  1663.     }
  1664. }
  1665.  
  1666.  
  1667. /*QUAKED trigger_elevator (0.3 0.1 0.6) (-8 -8 -8) (8 8 8)
  1668. */
  1669. void trigger_elevator_use (edict_t *self, edict_t *other, edict_t *activator)
  1670. {
  1671.     edict_t *target;
  1672.  
  1673.     if (self->movetarget->nextthink)
  1674.     {
  1675. //        gi.dprintf("elevator busy\n");
  1676.         return;
  1677.     }
  1678.  
  1679.     if (!other->pathtarget)
  1680.     {
  1681.         gi.dprintf("elevator used with no pathtarget\n");
  1682.         return;
  1683.     }
  1684.  
  1685.     target = G_PickTarget (other->pathtarget);
  1686.     if (!target)
  1687.     {
  1688.         gi.dprintf("elevator used with bad pathtarget: %s\n", other->pathtarget);
  1689.         return;
  1690.     }
  1691.  
  1692.     self->movetarget->target_ent = target;
  1693.     train_resume (self->movetarget);
  1694. }
  1695.  
  1696. void trigger_elevator_init (edict_t *self)
  1697. {
  1698.     if (!self->target)
  1699.     {
  1700.         gi.dprintf("trigger_elevator has no target\n");
  1701.         return;
  1702.     }
  1703.     self->movetarget = G_PickTarget (self->target);
  1704.     if (!self->movetarget)
  1705.     {
  1706.         gi.dprintf("trigger_elevator unable to find target %s\n", self->target);
  1707.         return;
  1708.     }
  1709.     if (strcmp(self->movetarget->classname, "func_train") != 0)
  1710.     {
  1711.         gi.dprintf("trigger_elevator target %s is not a train\n", self->target);
  1712.         return;
  1713.     }
  1714.  
  1715.     self->use = trigger_elevator_use;
  1716.     self->svflags = SVF_NOCLIENT;
  1717.  
  1718. }
  1719.  
  1720. void SP_trigger_elevator (edict_t *self)
  1721. {
  1722.     self->think = trigger_elevator_init;
  1723.     self->nextthink = level.time + FRAMETIME;
  1724. }
  1725.  
  1726.  
  1727. /*QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON
  1728. "wait"            base time between triggering all targets, default is 1
  1729. "random"        wait variance, default is 0
  1730.  
  1731. so, the basic time between firing is a random time between
  1732. (wait - random) and (wait + random)
  1733.  
  1734. "delay"            delay before first firing when turned on, default is 0
  1735.  
  1736. "pausetime"        additional delay used only the very first time
  1737.                 and only if spawned with START_ON
  1738.  
  1739. These can used but not touched.
  1740. */
  1741. void func_timer_think (edict_t *self)
  1742. {
  1743.     G_UseTargets (self, self->activator);
  1744.     self->nextthink = level.time + self->wait + crandom() * self->random;
  1745. }
  1746.  
  1747. void func_timer_use (edict_t *self, edict_t *other, edict_t *activator)
  1748. {
  1749.     self->activator = activator;
  1750.  
  1751.     // if on, turn it off
  1752.     if (self->nextthink)
  1753.     {
  1754.         self->nextthink = 0;
  1755.         return;
  1756.     }
  1757.  
  1758.     // turn it on
  1759.     if (self->delay)
  1760.         self->nextthink = level.time + self->delay;
  1761.     else
  1762.         func_timer_think (self);
  1763. }
  1764.  
  1765. void SP_func_timer (edict_t *self)
  1766. {
  1767.     if (!self->wait)
  1768.         self->wait = 1.0;
  1769.  
  1770.     self->use = func_timer_use;
  1771.     self->think = func_timer_think;
  1772.  
  1773.     if (self->random >= self->wait)
  1774.     {
  1775.         self->random = self->wait - FRAMETIME;
  1776.         gi.dprintf("func_timer at %s has random >= wait\n", vtos(self->s.origin));
  1777.     }
  1778.  
  1779.     if (self->spawnflags & 1)
  1780.     {
  1781.         self->nextthink = level.time + 1.0 + st.pausetime + self->delay + self->wait + crandom() * self->random;
  1782.         self->activator = self;
  1783.     }
  1784.  
  1785.     self->svflags = SVF_NOCLIENT;
  1786. }
  1787.  
  1788.  
  1789. /*QUAKED func_conveyor (0 .5 .8) ? START_ON TOGGLE
  1790. Conveyors are stationary brushes that move what's on them.
  1791. The brush should be have a surface with at least one current content enabled.
  1792. speed    default 100
  1793. */
  1794.  
  1795. void func_conveyor_use (edict_t *self, edict_t *other, edict_t *activator)
  1796. {
  1797.     if (self->spawnflags & 1)
  1798.     {
  1799.         self->speed = 0;
  1800.         self->spawnflags &= ~1;
  1801.     }
  1802.     else
  1803.     {
  1804.         self->speed = self->count;
  1805.         self->spawnflags |= 1;
  1806.     }
  1807.  
  1808.     if (!(self->spawnflags & 2))
  1809.         self->count = 0;
  1810. }
  1811.  
  1812. void SP_func_conveyor (edict_t *self)
  1813. {
  1814.     if (!self->speed)
  1815.         self->speed = 100;
  1816.  
  1817.     if (!(self->spawnflags & 1))
  1818.     {
  1819.         self->count = self->speed;
  1820.         self->speed = 0;
  1821.     }
  1822.  
  1823.     self->use = func_conveyor_use;
  1824.  
  1825.     gi.setmodel (self, self->model);
  1826.     self->solid = SOLID_BSP;
  1827.     gi.linkentity (self);
  1828. }
  1829.  
  1830.  
  1831. /*QUAKED func_door_secret (0 .5 .8) ? always_shoot 1st_left 1st_down
  1832. A secret door.  Slide back and then to the side.
  1833.  
  1834. open_once        doors never closes
  1835. 1st_left        1st move is left of arrow
  1836. 1st_down        1st move is down from arrow
  1837. always_shoot    door is shootebale even if targeted
  1838.  
  1839. "angle"        determines the direction
  1840. "dmg"        damage to inflic when blocked (default 2)
  1841. "wait"        how long to hold in the open position (default 5, -1 means hold)
  1842. */
  1843.  
  1844. #define SECRET_ALWAYS_SHOOT    1
  1845. #define SECRET_1ST_LEFT        2
  1846. #define SECRET_1ST_DOWN        4
  1847.  
  1848. void door_secret_move1 (edict_t *self);
  1849. void door_secret_move2 (edict_t *self);
  1850. void door_secret_move3 (edict_t *self);
  1851. void door_secret_move4 (edict_t *self);
  1852. void door_secret_move5 (edict_t *self);
  1853. void door_secret_move6 (edict_t *self);
  1854. void door_secret_done (edict_t *self);
  1855.  
  1856. void door_secret_use (edict_t *self, edict_t *other, edict_t *activator)
  1857. {
  1858.     // make sure we're not already moving
  1859.     if (!VectorCompare(self->s.origin, vec3_origin))
  1860.         return;
  1861.  
  1862.     Move_Calc (self, self->pos1, door_secret_move1);
  1863.     door_use_areaportals (self, true);
  1864. }
  1865.  
  1866. void door_secret_move1 (edict_t *self)
  1867. {
  1868.     self->nextthink = level.time + 1.0;
  1869.     self->think = door_secret_move2;
  1870. }
  1871.  
  1872. void door_secret_move2 (edict_t *self)
  1873. {
  1874.     Move_Calc (self, self->pos2, door_secret_move3);
  1875. }
  1876.  
  1877. void door_secret_move3 (edict_t *self)
  1878. {
  1879.     if (self->wait == -1)
  1880.         return;
  1881.     self->nextthink = level.time + self->wait;
  1882.     self->think = door_secret_move4;
  1883. }
  1884.  
  1885. void door_secret_move4 (edict_t *self)
  1886. {
  1887.     Move_Calc (self, self->pos1, door_secret_move5);
  1888. }
  1889.  
  1890. void door_secret_move5 (edict_t *self)
  1891. {
  1892.     self->nextthink = level.time + 1.0;
  1893.     self->think = door_secret_move6;
  1894. }
  1895.  
  1896. void door_secret_move6 (edict_t *self)
  1897. {
  1898.     Move_Calc (self, vec3_origin, door_secret_done);
  1899. }
  1900.  
  1901. void door_secret_done (edict_t *self)
  1902. {
  1903.     if (!(self->targetname) || (self->spawnflags & SECRET_ALWAYS_SHOOT))
  1904.     {
  1905.         self->health = 0;
  1906.         self->takedamage = DAMAGE_YES;
  1907.     }
  1908.     door_use_areaportals (self, false);
  1909. }
  1910.  
  1911. void door_secret_blocked  (edict_t *self, edict_t *other)
  1912. {
  1913.     if (!(other->svflags & SVF_MONSTER) && (!other->client))
  1914.     {
  1915.         // give it a chance to go away on it's own terms (like gibs)
  1916.         T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0);
  1917.         // if it's still there, nuke it
  1918.         BecomeExplosion1 (other);
  1919.         return;
  1920.     }
  1921.  
  1922.     if (level.time < self->touch_debounce_time)
  1923.         return;
  1924.     self->touch_debounce_time = level.time + 0.5;
  1925.  
  1926.     T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0);
  1927. }
  1928.  
  1929. void door_secret_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  1930. {
  1931.     self->takedamage = DAMAGE_NO;
  1932.     door_secret_use (self, attacker, attacker);
  1933. }
  1934.  
  1935. void SP_func_door_secret (edict_t *ent)
  1936. {
  1937.     vec3_t    forward, right, up;
  1938.     float    side;
  1939.     float    width;
  1940.     float    length;
  1941.  
  1942.     ent->moveinfo.sound_start = gi.soundindex  ("doors/dr1_strt.wav");
  1943.     ent->moveinfo.sound_middle = gi.soundindex  ("doors/dr1_mid.wav");
  1944.     ent->moveinfo.sound_end = gi.soundindex  ("doors/dr1_end.wav");
  1945.  
  1946.     ent->movetype = MOVETYPE_PUSH;
  1947.     ent->solid = SOLID_BSP;
  1948.     gi.setmodel (ent, ent->model);
  1949.  
  1950.     ent->blocked = door_secret_blocked;
  1951.     ent->use = door_secret_use;
  1952.  
  1953.     if (!(ent->targetname) || (ent->spawnflags & SECRET_ALWAYS_SHOOT))
  1954.     {
  1955.         ent->health = 0;
  1956.         ent->takedamage = DAMAGE_YES;
  1957.         ent->die = door_secret_die;
  1958.     }
  1959.  
  1960.     if (!ent->dmg)
  1961.         ent->dmg = 2;
  1962.  
  1963.     if (!ent->wait)
  1964.         ent->wait = 5;
  1965.  
  1966.     ent->moveinfo.accel =
  1967.     ent->moveinfo.decel =
  1968.     ent->moveinfo.speed = 50;
  1969.  
  1970.     // calculate positions
  1971.     AngleVectors (ent->s.angles, forward, right, up);
  1972.     VectorClear (ent->s.angles);
  1973.     side = 1.0 - (ent->spawnflags & SECRET_1ST_LEFT);
  1974.     if (ent->spawnflags & SECRET_1ST_DOWN)
  1975.         width = fabs(DotProduct(up, ent->size));
  1976.     else
  1977.         width = fabs(DotProduct(right, ent->size));
  1978.     length = fabs(DotProduct(forward, ent->size));
  1979.     if (ent->spawnflags & SECRET_1ST_DOWN)
  1980.         VectorMA (ent->s.origin, -1 * width, up, ent->pos1);
  1981.     else
  1982.         VectorMA (ent->s.origin, side * width, right, ent->pos1);
  1983.     VectorMA (ent->pos1, length, forward, ent->pos2);
  1984.  
  1985.     if (ent->health)
  1986.     {
  1987.         ent->takedamage = DAMAGE_YES;
  1988.         ent->die = door_killed;
  1989.         ent->max_health = ent->health;
  1990.     }
  1991.     else if (ent->targetname && ent->message)
  1992.     {
  1993.         gi.soundindex ("misc/talk.wav");
  1994.         ent->touch = door_touch;
  1995.     }
  1996.     
  1997.     ent->classname = "func_door";
  1998.  
  1999.     gi.linkentity (ent);
  2000. }
  2001.  
  2002.  
  2003. /*QUAKED func_killbox (1 0 0) ?
  2004. Kills everything inside when fired, irrespective of protection.
  2005. */
  2006. void use_killbox (edict_t *self, edict_t *other, edict_t *activator)
  2007. {
  2008.     KillBox (self);
  2009. }
  2010.  
  2011. void SP_func_killbox (edict_t *ent)
  2012. {
  2013.     gi.setmodel (ent, ent->model);
  2014.     ent->use = use_killbox;
  2015.     ent->svflags = SVF_NOCLIENT;
  2016. }
  2017.  
  2018.