Annotation of quake2/game/g_func.c, revision 1.1.1.3

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.