Annotation of quake2/rogue/g_func.c, revision 1.1

1.1     ! root        1: #include "g_local.h"
        !             2: 
        !             3: /*
        !             4: =========================================================
        !             5: 
        !             6:   PLATS
        !             7: 
        !             8:   movement options:
        !             9: 
        !            10:   linear
        !            11:   smooth start, hard stop
        !            12:   smooth start, smooth stop
        !            13: 
        !            14:   start
        !            15:   end
        !            16:   acceleration
        !            17:   speed
        !            18:   deceleration
        !            19:   begin sound
        !            20:   end sound
        !            21:   target fired when reaching end
        !            22:   wait at end
        !            23: 
        !            24:   object characteristics that use move segments
        !            25:   ---------------------------------------------
        !            26:   movetype_push, or movetype_stop
        !            27:   action when touched
        !            28:   action when blocked
        !            29:   action when used
        !            30:        disabled?
        !            31:   auto trigger spawning
        !            32: 
        !            33: 
        !            34: =========================================================
        !            35: */
        !            36: 
        !            37: #define PLAT_LOW_TRIGGER       1
        !            38: 
        !            39: //====
        !            40: //PGM
        !            41: #define PLAT2_TOGGLE                   2
        !            42: #define PLAT2_TOP                              4
        !            43: #define PLAT2_TRIGGER_TOP              8
        !            44: #define PLAT2_TRIGGER_BOTTOM   16
        !            45: #define PLAT2_BOX_LIFT                 32
        !            46: 
        !            47: void plat2_spawn_danger_area (edict_t *ent);
        !            48: void plat2_kill_danger_area (edict_t *ent);
        !            49: 
        !            50: //PGM
        !            51: //====
        !            52: 
        !            53: #define        STATE_TOP                       0
        !            54: #define        STATE_BOTTOM            1
        !            55: #define STATE_UP                       2
        !            56: #define STATE_DOWN                     3
        !            57: 
        !            58: #define DOOR_START_OPEN                1
        !            59: #define DOOR_REVERSE           2
        !            60: #define DOOR_CRUSHER           4
        !            61: #define DOOR_NOMONSTER         8
        !            62: #define DOOR_TOGGLE                    32
        !            63: #define DOOR_X_AXIS                    64
        !            64: #define DOOR_Y_AXIS                    128
        !            65: // !easy                                       256
        !            66: // !med                                                512
        !            67: // !hard                                       1024
        !            68: // !dm                                         2048
        !            69: // !coop                                       4096
        !            70: #define DOOR_INACTIVE          8192
        !            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: //PGM          accelerate as needed
        !           182:        if(ent->moveinfo.speed < ent->speed)
        !           183:        {
        !           184:                ent->moveinfo.speed += ent->accel;
        !           185:                if(ent->moveinfo.speed > ent->speed)
        !           186:                        ent->moveinfo.speed = ent->speed;
        !           187:        }
        !           188: //PGM
        !           189: 
        !           190:        // set destdelta to the vector needed to move
        !           191:        if (ent->moveinfo.state == STATE_UP)
        !           192:                VectorSubtract (ent->moveinfo.end_angles, ent->s.angles, destdelta);
        !           193:        else
        !           194:                VectorSubtract (ent->moveinfo.start_angles, ent->s.angles, destdelta);
        !           195:        
        !           196:        // calculate length of vector
        !           197:        len = VectorLength (destdelta);
        !           198:        
        !           199:        // divide by speed to get time to reach dest
        !           200:        traveltime = len / ent->moveinfo.speed;
        !           201: 
        !           202:        if (traveltime < FRAMETIME)
        !           203:        {
        !           204:                AngleMove_Final (ent);
        !           205:                return;
        !           206:        }
        !           207: 
        !           208:        frames = floor(traveltime / FRAMETIME);
        !           209: 
        !           210:        // scale the destdelta vector by the time spent traveling to get velocity
        !           211:        VectorScale (destdelta, 1.0 / traveltime, ent->avelocity);
        !           212: 
        !           213: //PGM
        !           214:        // if we're done accelerating, act as a normal rotation
        !           215:        if(ent->moveinfo.speed >= ent->speed)
        !           216:        {
        !           217:                // set nextthink to trigger a think when dest is reached
        !           218:                ent->nextthink = level.time + frames * FRAMETIME;
        !           219:                ent->think = AngleMove_Final;
        !           220:        }
        !           221:        else
        !           222:        {
        !           223:                ent->nextthink = level.time + FRAMETIME;
        !           224:                ent->think = AngleMove_Begin;
        !           225:        }
        !           226: //PGM
        !           227: }
        !           228: 
        !           229: void AngleMove_Calc (edict_t *ent, void(*func)(edict_t*))
        !           230: {
        !           231:        VectorClear (ent->avelocity);
        !           232:        ent->moveinfo.endfunc = func;
        !           233: 
        !           234: //PGM
        !           235:        // if we're supposed to accelerate, this will tell anglemove_begin to do so
        !           236:        if(ent->accel != ent->speed)
        !           237:                ent->moveinfo.speed = 0;
        !           238: //PGM
        !           239: 
        !           240:        if (level.current_entity == ((ent->flags & FL_TEAMSLAVE) ? ent->teammaster : ent))
        !           241:        {
        !           242:                AngleMove_Begin (ent);
        !           243:        }
        !           244:        else
        !           245:        {
        !           246:                ent->nextthink = level.time + FRAMETIME;
        !           247:                ent->think = AngleMove_Begin;
        !           248:        }
        !           249: }
        !           250: 
        !           251: 
        !           252: /*
        !           253: ==============
        !           254: Think_AccelMove
        !           255: 
        !           256: The team has completed a frame of movement, so
        !           257: change the speed for the next frame
        !           258: ==============
        !           259: */
        !           260: #define AccelerationDistance(target, rate)     (target * ((target / rate) + 1) / 2)
        !           261: 
        !           262: void plat_CalcAcceleratedMove(moveinfo_t *moveinfo)
        !           263: {
        !           264:        float   accel_dist;
        !           265:        float   decel_dist;
        !           266: 
        !           267:        moveinfo->move_speed = moveinfo->speed;
        !           268: 
        !           269:        if (moveinfo->remaining_distance < moveinfo->accel)
        !           270:        {
        !           271:                moveinfo->current_speed = moveinfo->remaining_distance;
        !           272:                return;
        !           273:        }
        !           274: 
        !           275:        accel_dist = AccelerationDistance (moveinfo->speed, moveinfo->accel);
        !           276:        decel_dist = AccelerationDistance (moveinfo->speed, moveinfo->decel);
        !           277: 
        !           278:        if ((moveinfo->remaining_distance - accel_dist - decel_dist) < 0)
        !           279:        {
        !           280:                float   f;
        !           281: 
        !           282:                f = (moveinfo->accel + moveinfo->decel) / (moveinfo->accel * moveinfo->decel);
        !           283:                moveinfo->move_speed = (-2 + sqrt(4 - 4 * f * (-2 * moveinfo->remaining_distance))) / (2 * f);
        !           284:                decel_dist = AccelerationDistance (moveinfo->move_speed, moveinfo->decel);
        !           285:        }
        !           286: 
        !           287:        moveinfo->decel_distance = decel_dist;
        !           288: };
        !           289: 
        !           290: void plat_Accelerate (moveinfo_t *moveinfo)
        !           291: {
        !           292:        // are we decelerating?
        !           293:        if (moveinfo->remaining_distance <= moveinfo->decel_distance)
        !           294:        {
        !           295:                if (moveinfo->remaining_distance < moveinfo->decel_distance)
        !           296:                {
        !           297:                        if (moveinfo->next_speed)
        !           298:                        {
        !           299:                                moveinfo->current_speed = moveinfo->next_speed;
        !           300:                                moveinfo->next_speed = 0;
        !           301:                                return;
        !           302:                        }
        !           303:                        if (moveinfo->current_speed > moveinfo->decel)
        !           304:                                moveinfo->current_speed -= moveinfo->decel;
        !           305:                }
        !           306:                return;
        !           307:        }
        !           308: 
        !           309:        // are we at full speed and need to start decelerating during this move?
        !           310:        if (moveinfo->current_speed == moveinfo->move_speed)
        !           311:                if ((moveinfo->remaining_distance - moveinfo->current_speed) < moveinfo->decel_distance)
        !           312:                {
        !           313:                        float   p1_distance;
        !           314:                        float   p2_distance;
        !           315:                        float   distance;
        !           316: 
        !           317:                        p1_distance = moveinfo->remaining_distance - moveinfo->decel_distance;
        !           318:                        p2_distance = moveinfo->move_speed * (1.0 - (p1_distance / moveinfo->move_speed));
        !           319:                        distance = p1_distance + p2_distance;
        !           320:                        moveinfo->current_speed = moveinfo->move_speed;
        !           321:                        moveinfo->next_speed = moveinfo->move_speed - moveinfo->decel * (p2_distance / distance);
        !           322:                        return;
        !           323:                }
        !           324: 
        !           325:        // are we accelerating?
        !           326:        if (moveinfo->current_speed < moveinfo->speed)
        !           327:        {
        !           328:                float   old_speed;
        !           329:                float   p1_distance;
        !           330:                float   p1_speed;
        !           331:                float   p2_distance;
        !           332:                float   distance;
        !           333: 
        !           334:                old_speed = moveinfo->current_speed;
        !           335: 
        !           336:                // figure simple acceleration up to move_speed
        !           337:                moveinfo->current_speed += moveinfo->accel;
        !           338:                if (moveinfo->current_speed > moveinfo->speed)
        !           339:                        moveinfo->current_speed = moveinfo->speed;
        !           340: 
        !           341:                // are we accelerating throughout this entire move?
        !           342:                if ((moveinfo->remaining_distance - moveinfo->current_speed) >= moveinfo->decel_distance)
        !           343:                        return;
        !           344: 
        !           345:                // during this move we will accelrate from current_speed to move_speed
        !           346:                // and cross over the decel_distance; figure the average speed for the
        !           347:                // entire move
        !           348:                p1_distance = moveinfo->remaining_distance - moveinfo->decel_distance;
        !           349:                p1_speed = (old_speed + moveinfo->move_speed) / 2.0;
        !           350:                p2_distance = moveinfo->move_speed * (1.0 - (p1_distance / p1_speed));
        !           351:                distance = p1_distance + p2_distance;
        !           352:                moveinfo->current_speed = (p1_speed * (p1_distance / distance)) + (moveinfo->move_speed * (p2_distance / distance));
        !           353:                moveinfo->next_speed = moveinfo->move_speed - moveinfo->decel * (p2_distance / distance);
        !           354:                return;
        !           355:        }
        !           356: 
        !           357:        // we are at constant velocity (move_speed)
        !           358:        return;
        !           359: };
        !           360: 
        !           361: void Think_AccelMove (edict_t *ent)
        !           362: {
        !           363:        ent->moveinfo.remaining_distance -= ent->moveinfo.current_speed;
        !           364: 
        !           365:        // PGM 04/21/98  - this should fix sthoms' sinking drop pod. Hopefully it wont break stuff.
        !           366: //     if (ent->moveinfo.current_speed == 0)           // starting or blocked
        !           367:                plat_CalcAcceleratedMove(&ent->moveinfo);
        !           368: 
        !           369:        plat_Accelerate (&ent->moveinfo);
        !           370: 
        !           371:        // will the entire move complete on next frame?
        !           372:        if (ent->moveinfo.remaining_distance <= ent->moveinfo.current_speed)
        !           373:        {
        !           374:                Move_Final (ent);
        !           375:                return;
        !           376:        }
        !           377: 
        !           378:        VectorScale (ent->moveinfo.dir, ent->moveinfo.current_speed*10, ent->velocity);
        !           379:        ent->nextthink = level.time + FRAMETIME;
        !           380:        ent->think = Think_AccelMove;
        !           381: }
        !           382: 
        !           383: 
        !           384: void plat_go_down (edict_t *ent);
        !           385: 
        !           386: void plat_hit_top (edict_t *ent)
        !           387: {
        !           388:        if (!(ent->flags & FL_TEAMSLAVE))
        !           389:        {
        !           390:                if (ent->moveinfo.sound_end)
        !           391:                        gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_end, 1, ATTN_STATIC, 0);
        !           392:                ent->s.sound = 0;
        !           393:        }
        !           394:        ent->moveinfo.state = STATE_TOP;
        !           395: 
        !           396:        ent->think = plat_go_down;
        !           397:        ent->nextthink = level.time + 3;
        !           398: }
        !           399: 
        !           400: void plat_hit_bottom (edict_t *ent)
        !           401: {
        !           402:        if (!(ent->flags & FL_TEAMSLAVE))
        !           403:        {
        !           404:                if (ent->moveinfo.sound_end)
        !           405:                        gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_end, 1, ATTN_STATIC, 0);
        !           406:                ent->s.sound = 0;
        !           407:        }
        !           408:        ent->moveinfo.state = STATE_BOTTOM;
        !           409:        
        !           410:        plat2_kill_danger_area (ent);           // PGM
        !           411: }
        !           412: 
        !           413: void plat_go_down (edict_t *ent)
        !           414: {
        !           415:        if (!(ent->flags & FL_TEAMSLAVE))
        !           416:        {
        !           417:                if (ent->moveinfo.sound_start)
        !           418:                        gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_start, 1, ATTN_STATIC, 0);
        !           419:                ent->s.sound = ent->moveinfo.sound_middle;
        !           420:        }
        !           421:        ent->moveinfo.state = STATE_DOWN;
        !           422:        Move_Calc (ent, ent->moveinfo.end_origin, plat_hit_bottom);
        !           423: }
        !           424: 
        !           425: void plat_go_up (edict_t *ent)
        !           426: {
        !           427:        if (!(ent->flags & FL_TEAMSLAVE))
        !           428:        {
        !           429:                if (ent->moveinfo.sound_start)
        !           430:                        gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_start, 1, ATTN_STATIC, 0);
        !           431:                ent->s.sound = ent->moveinfo.sound_middle;
        !           432:        }
        !           433:        ent->moveinfo.state = STATE_UP;
        !           434:        Move_Calc (ent, ent->moveinfo.start_origin, plat_hit_top);
        !           435:        
        !           436:        plat2_spawn_danger_area(ent);   // PGM
        !           437: }
        !           438: 
        !           439: void plat_blocked (edict_t *self, edict_t *other)
        !           440: {
        !           441:        if (!(other->svflags & SVF_MONSTER) && (!other->client) )
        !           442:        {
        !           443:                // give it a chance to go away on it's own terms (like gibs)
        !           444:                T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
        !           445:                // if it's still there, nuke it
        !           446:                if (other && other->inuse)              // PGM
        !           447:                        BecomeExplosion1 (other);
        !           448:                return;
        !           449:        }
        !           450: 
        !           451: //PGM
        !           452:        // gib dead things
        !           453:        if(other->health < 1)
        !           454:        {
        !           455:                T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, 100, 1, 0, MOD_CRUSH);
        !           456:        }
        !           457: //PGM
        !           458: 
        !           459:        T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
        !           460: 
        !           461:        if (self->moveinfo.state == STATE_UP)
        !           462:                plat_go_down (self);
        !           463:        else if (self->moveinfo.state == STATE_DOWN)
        !           464:                plat_go_up (self);
        !           465: }
        !           466: 
        !           467: 
        !           468: void Use_Plat (edict_t *ent, edict_t *other, edict_t *activator)
        !           469: { 
        !           470: //======
        !           471: //ROGUE
        !           472:        // if a monster is using us, then allow the activity when stopped.
        !           473:        if (other->svflags & SVF_MONSTER)
        !           474:        {
        !           475:                if (ent->moveinfo.state == STATE_TOP)
        !           476:                        plat_go_down (ent);
        !           477:                else if (ent->moveinfo.state == STATE_BOTTOM)
        !           478:                        plat_go_up (ent);
        !           479: 
        !           480:                return;
        !           481:        }
        !           482: //ROGUE
        !           483: //======
        !           484: 
        !           485:        if (ent->think)
        !           486:                return;         // already down
        !           487:        plat_go_down (ent);
        !           488: }
        !           489: 
        !           490: 
        !           491: void Touch_Plat_Center (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
        !           492: {
        !           493:        if (!other->client)
        !           494:                return;
        !           495:                
        !           496:        if (other->health <= 0)
        !           497:                return;
        !           498: 
        !           499:        ent = ent->enemy;       // now point at the plat, not the trigger
        !           500:        if (ent->moveinfo.state == STATE_BOTTOM)
        !           501:                plat_go_up (ent);
        !           502:        else if (ent->moveinfo.state == STATE_TOP)
        !           503:                ent->nextthink = level.time + 1;        // the player is still on the plat, so delay going down
        !           504: }
        !           505: 
        !           506: // PGM - plat2's change the trigger field
        !           507: //void plat_spawn_inside_trigger (edict_t *ent)
        !           508: edict_t *plat_spawn_inside_trigger (edict_t *ent)
        !           509: {
        !           510:        edict_t *trigger;
        !           511:        vec3_t  tmin, tmax;
        !           512: 
        !           513: //
        !           514: // middle trigger
        !           515: //     
        !           516:        trigger = G_Spawn();
        !           517:        trigger->touch = Touch_Plat_Center;
        !           518:        trigger->movetype = MOVETYPE_NONE;
        !           519:        trigger->solid = SOLID_TRIGGER;
        !           520:        trigger->enemy = ent;
        !           521:        
        !           522:        tmin[0] = ent->mins[0] + 25;
        !           523:        tmin[1] = ent->mins[1] + 25;
        !           524:        tmin[2] = ent->mins[2];
        !           525: 
        !           526:        tmax[0] = ent->maxs[0] - 25;
        !           527:        tmax[1] = ent->maxs[1] - 25;
        !           528:        tmax[2] = ent->maxs[2] + 8;
        !           529: 
        !           530:        tmin[2] = tmax[2] - (ent->pos1[2] - ent->pos2[2] + st.lip);
        !           531: 
        !           532:        if (ent->spawnflags & PLAT_LOW_TRIGGER)
        !           533:                tmax[2] = tmin[2] + 8;
        !           534:        
        !           535:        if (tmax[0] - tmin[0] <= 0)
        !           536:        {
        !           537:                tmin[0] = (ent->mins[0] + ent->maxs[0]) *0.5;
        !           538:                tmax[0] = tmin[0] + 1;
        !           539:        }
        !           540:        if (tmax[1] - tmin[1] <= 0)
        !           541:        {
        !           542:                tmin[1] = (ent->mins[1] + ent->maxs[1]) *0.5;
        !           543:                tmax[1] = tmin[1] + 1;
        !           544:        }
        !           545:        
        !           546:        VectorCopy (tmin, trigger->mins);
        !           547:        VectorCopy (tmax, trigger->maxs);
        !           548: 
        !           549:        gi.linkentity (trigger);
        !           550: 
        !           551:        return trigger;                 // PGM 11/17/97
        !           552: }
        !           553: 
        !           554: 
        !           555: /*QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER
        !           556: speed  default 150
        !           557: 
        !           558: Plats are always drawn in the extended position, so they will light correctly.
        !           559: 
        !           560: 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.
        !           561: 
        !           562: "speed"        overrides default 200.
        !           563: "accel" overrides default 500
        !           564: "lip"  overrides default 8 pixel lip
        !           565: 
        !           566: If the "height" key is set, that will determine the amount the plat moves, instead of being implicitly determoveinfoned by the model's height.
        !           567: 
        !           568: Set "sounds" to one of the following:
        !           569: 1) base fast
        !           570: 2) chain slow
        !           571: */
        !           572: void SP_func_plat (edict_t *ent)
        !           573: {
        !           574:        VectorClear (ent->s.angles);
        !           575:        ent->solid = SOLID_BSP;
        !           576:        ent->movetype = MOVETYPE_PUSH;
        !           577: 
        !           578:        gi.setmodel (ent, ent->model);
        !           579: 
        !           580:        ent->blocked = plat_blocked;
        !           581: 
        !           582:        if (!ent->speed)
        !           583:                ent->speed = 20;
        !           584:        else
        !           585:                ent->speed *= 0.1;
        !           586: 
        !           587:        if (!ent->accel)
        !           588:                ent->accel = 5;
        !           589:        else
        !           590:                ent->accel *= 0.1;
        !           591: 
        !           592:        if (!ent->decel)
        !           593:                ent->decel = 5;
        !           594:        else
        !           595:                ent->decel *= 0.1;
        !           596: 
        !           597:        if (!ent->dmg)
        !           598:                ent->dmg = 2;
        !           599: 
        !           600:        if (!st.lip)
        !           601:                st.lip = 8;
        !           602: 
        !           603:        // pos1 is the top position, pos2 is the bottom
        !           604:        VectorCopy (ent->s.origin, ent->pos1);
        !           605:        VectorCopy (ent->s.origin, ent->pos2);
        !           606:        if (st.height)
        !           607:                ent->pos2[2] -= st.height;
        !           608:        else
        !           609:                ent->pos2[2] -= (ent->maxs[2] - ent->mins[2]) - st.lip;
        !           610: 
        !           611:        ent->use = Use_Plat;
        !           612: 
        !           613:        plat_spawn_inside_trigger (ent);        // the "start moving" trigger   
        !           614: 
        !           615:        if (ent->targetname)
        !           616:        {
        !           617:                ent->moveinfo.state = STATE_UP;
        !           618:        }
        !           619:        else
        !           620:        {
        !           621:                VectorCopy (ent->pos2, ent->s.origin);
        !           622:                gi.linkentity (ent);
        !           623:                ent->moveinfo.state = STATE_BOTTOM;
        !           624:        }
        !           625: 
        !           626:        ent->moveinfo.speed = ent->speed;
        !           627:        ent->moveinfo.accel = ent->accel;
        !           628:        ent->moveinfo.decel = ent->decel;
        !           629:        ent->moveinfo.wait = ent->wait;
        !           630:        VectorCopy (ent->pos1, ent->moveinfo.start_origin);
        !           631:        VectorCopy (ent->s.angles, ent->moveinfo.start_angles);
        !           632:        VectorCopy (ent->pos2, ent->moveinfo.end_origin);
        !           633:        VectorCopy (ent->s.angles, ent->moveinfo.end_angles);
        !           634: 
        !           635:        ent->moveinfo.sound_start = gi.soundindex ("plats/pt1_strt.wav");
        !           636:        ent->moveinfo.sound_middle = gi.soundindex ("plats/pt1_mid.wav");
        !           637:        ent->moveinfo.sound_end = gi.soundindex ("plats/pt1_end.wav");
        !           638: }
        !           639: 
        !           640: // ==========================================
        !           641: // PLAT 2
        !           642: // ==========================================
        !           643: #define PLAT2_CALLED           1
        !           644: #define PLAT2_MOVING           2
        !           645: #define PLAT2_WAITING          4
        !           646: 
        !           647: void plat2_go_down (edict_t *ent);
        !           648: void plat2_go_up (edict_t *ent);
        !           649: 
        !           650: void plat2_spawn_danger_area (edict_t *ent)
        !           651: {
        !           652:        vec3_t  mins, maxs;
        !           653: 
        !           654:        VectorCopy(ent->mins, mins);
        !           655:        VectorCopy(ent->maxs, maxs);
        !           656:        maxs[2] = ent->mins[2] + 64;
        !           657: 
        !           658:        SpawnBadArea(mins, maxs, 0, ent);
        !           659: }
        !           660: 
        !           661: void plat2_kill_danger_area (edict_t *ent)
        !           662: {
        !           663:        edict_t *t;
        !           664: 
        !           665:        t = NULL;
        !           666:        while ((t = G_Find (t, FOFS(classname), "bad_area")))
        !           667:        {
        !           668:                if(t->owner == ent)
        !           669:                        G_FreeEdict(t);
        !           670:        }
        !           671: }
        !           672: 
        !           673: void plat2_hit_top (edict_t *ent)
        !           674: {
        !           675:        if (!(ent->flags & FL_TEAMSLAVE))
        !           676:        {
        !           677:                if (ent->moveinfo.sound_end)
        !           678:                        gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_end, 1, ATTN_STATIC, 0);
        !           679:                ent->s.sound = 0;
        !           680:        }
        !           681:        ent->moveinfo.state = STATE_TOP;
        !           682: 
        !           683:        if(ent->plat2flags & PLAT2_CALLED)
        !           684:        {
        !           685:                ent->plat2flags = PLAT2_WAITING;
        !           686:                if(!(ent->spawnflags & PLAT2_TOGGLE))
        !           687:                {
        !           688:                        ent->think = plat2_go_down;
        !           689:                        ent->nextthink = level.time + 5.0;
        !           690:                }
        !           691:                if(deathmatch->value)
        !           692:                        ent->last_move_time = level.time - 1.0;
        !           693:                else
        !           694:                        ent->last_move_time = level.time - 2.0;
        !           695:        }
        !           696:        else if(!(ent->spawnflags & PLAT2_TOP) && !(ent->spawnflags & PLAT2_TOGGLE))
        !           697:        {
        !           698:                ent->plat2flags = 0;
        !           699:                ent->think = plat2_go_down;
        !           700:                ent->nextthink = level.time + 2.0;
        !           701:                ent->last_move_time = level.time;
        !           702:        }
        !           703:        else
        !           704:        {
        !           705:                ent->plat2flags = 0;
        !           706:                ent->last_move_time = level.time;
        !           707:        }
        !           708: 
        !           709:        G_UseTargets (ent, ent);
        !           710: }
        !           711: 
        !           712: void plat2_hit_bottom (edict_t *ent)
        !           713: {
        !           714:        if (!(ent->flags & FL_TEAMSLAVE))
        !           715:        {
        !           716:                if (ent->moveinfo.sound_end)
        !           717:                        gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_end, 1, ATTN_STATIC, 0);
        !           718:                ent->s.sound = 0;
        !           719:        }
        !           720:        ent->moveinfo.state = STATE_BOTTOM;
        !           721:        
        !           722:        if(ent->plat2flags & PLAT2_CALLED)
        !           723:        {
        !           724:                ent->plat2flags = PLAT2_WAITING;
        !           725:                if(!(ent->spawnflags & PLAT2_TOGGLE))
        !           726:                {
        !           727:                        ent->think = plat2_go_up;
        !           728:                        ent->nextthink = level.time + 5.0;
        !           729:                }
        !           730:                if(deathmatch->value)
        !           731:                        ent->last_move_time = level.time - 1.0;
        !           732:                else
        !           733:                        ent->last_move_time = level.time - 2.0;
        !           734:        }
        !           735:        else if ((ent->spawnflags & PLAT2_TOP) && !(ent->spawnflags & PLAT2_TOGGLE))
        !           736:        {
        !           737:                ent->plat2flags = 0;
        !           738:                ent->think = plat2_go_up;
        !           739:                ent->nextthink = level.time + 2.0;
        !           740:                ent->last_move_time = level.time;
        !           741:        }
        !           742:        else
        !           743:        {
        !           744:                ent->plat2flags = 0;
        !           745:                ent->last_move_time = level.time;
        !           746:        }
        !           747: 
        !           748:        plat2_kill_danger_area (ent);
        !           749:        G_UseTargets (ent, ent);
        !           750: }
        !           751: 
        !           752: void plat2_go_down (edict_t *ent)
        !           753: {
        !           754:        if (!(ent->flags & FL_TEAMSLAVE))
        !           755:        {
        !           756:                if (ent->moveinfo.sound_start)
        !           757:                        gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_start, 1, ATTN_STATIC, 0);
        !           758:                ent->s.sound = ent->moveinfo.sound_middle;
        !           759:        }
        !           760:        ent->moveinfo.state = STATE_DOWN;
        !           761:        ent->plat2flags |= PLAT2_MOVING;
        !           762: 
        !           763:        Move_Calc (ent, ent->moveinfo.end_origin, plat2_hit_bottom);
        !           764: }
        !           765: 
        !           766: void plat2_go_up (edict_t *ent)
        !           767: {
        !           768:        if (!(ent->flags & FL_TEAMSLAVE))
        !           769:        {
        !           770:                if (ent->moveinfo.sound_start)
        !           771:                        gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_start, 1, ATTN_STATIC, 0);
        !           772:                ent->s.sound = ent->moveinfo.sound_middle;
        !           773:        }
        !           774:        ent->moveinfo.state = STATE_UP;
        !           775:        ent->plat2flags |= PLAT2_MOVING;
        !           776: 
        !           777:        plat2_spawn_danger_area(ent);
        !           778: 
        !           779:        Move_Calc (ent, ent->moveinfo.start_origin, plat2_hit_top);
        !           780: }
        !           781: 
        !           782: void plat2_operate (edict_t *ent, edict_t *other)
        !           783: {
        !           784:        int             otherState;
        !           785:        float   pauseTime;
        !           786:        float   platCenter;
        !           787:        edict_t *trigger;
        !           788: 
        !           789:        trigger = ent;
        !           790:        ent = ent->enemy;       // now point at the plat, not the trigger
        !           791: 
        !           792:        if (ent->plat2flags & PLAT2_MOVING)
        !           793:                return;
        !           794: 
        !           795:        if ((ent->last_move_time + 2) > level.time)
        !           796:                return;
        !           797: 
        !           798:        platCenter = (trigger->absmin[2] + trigger->absmax[2]) / 2;
        !           799: 
        !           800:        if(ent->moveinfo.state == STATE_TOP)
        !           801:        {
        !           802:                otherState = STATE_TOP;
        !           803:                if(ent->spawnflags & PLAT2_BOX_LIFT)
        !           804:                {
        !           805:                        if(platCenter > other->s.origin[2])
        !           806:                                otherState = STATE_BOTTOM;
        !           807:                }
        !           808:                else
        !           809:                {
        !           810:                        if(trigger->absmax[2] > other->s.origin[2])
        !           811:                                otherState = STATE_BOTTOM;
        !           812:                }
        !           813:        }
        !           814:        else
        !           815:        {
        !           816:                otherState = STATE_BOTTOM;
        !           817:                if(other->s.origin[2] > platCenter)
        !           818:                        otherState = STATE_TOP;
        !           819:        }
        !           820: 
        !           821:        ent->plat2flags = PLAT2_MOVING;
        !           822: 
        !           823:        if(deathmatch->value)
        !           824:                pauseTime = 0.3;
        !           825:        else
        !           826:                pauseTime = 0.5;
        !           827: 
        !           828:        if(ent->moveinfo.state != otherState)
        !           829:        {
        !           830:                ent->plat2flags |= PLAT2_CALLED;
        !           831:                pauseTime = 0.1;
        !           832:        }
        !           833: 
        !           834:        ent->last_move_time = level.time;
        !           835:        
        !           836:        if(ent->moveinfo.state == STATE_BOTTOM)
        !           837:        {
        !           838:                ent->think = plat2_go_up;
        !           839:                ent->nextthink = level.time + pauseTime;
        !           840:        }
        !           841:        else
        !           842:        {
        !           843:                ent->think = plat2_go_down;
        !           844:                ent->nextthink = level.time + pauseTime;
        !           845:        }
        !           846: }
        !           847: 
        !           848: void Touch_Plat_Center2 (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
        !           849: {
        !           850:        // this requires monsters to actively trigger plats, not just step on them.
        !           851: 
        !           852:        //FIXME - commented out for E3
        !           853:        //if (!other->client)
        !           854:        //      return;
        !           855:                
        !           856:        if (other->health <= 0)
        !           857:                return;
        !           858: 
        !           859:        // PMM - don't let non-monsters activate plat2s
        !           860:        if ((!(other->svflags & SVF_MONSTER)) && (!other->client))
        !           861:                return;
        !           862:        
        !           863:        plat2_operate(ent, other);
        !           864: }
        !           865: 
        !           866: void plat2_blocked (edict_t *self, edict_t *other)
        !           867: {
        !           868:        if (!(other->svflags & SVF_MONSTER) && (!other->client))
        !           869:        {
        !           870:                // give it a chance to go away on it's own terms (like gibs)
        !           871:                T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
        !           872:                // if it's still there, nuke it
        !           873:                if(other && other->inuse)
        !           874:                        BecomeExplosion1 (other);
        !           875:                return;
        !           876:        }
        !           877: 
        !           878:        // gib dead things
        !           879:        if(other->health < 1)
        !           880:        {
        !           881:                T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, 100, 1, 0, MOD_CRUSH);
        !           882:        }
        !           883: 
        !           884:        T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
        !           885: 
        !           886:        if (self->moveinfo.state == STATE_UP)
        !           887:                plat2_go_down (self);
        !           888:        else if (self->moveinfo.state == STATE_DOWN)
        !           889:                plat2_go_up (self);
        !           890: }
        !           891: 
        !           892: void Use_Plat2 (edict_t *ent, edict_t *other, edict_t *activator)
        !           893: { 
        !           894:        edict_t         *trigger;
        !           895:        int                     i;
        !           896: 
        !           897:        if(ent->moveinfo.state > STATE_BOTTOM)
        !           898:                return;
        !           899:        if((ent->last_move_time + 2) > level.time)
        !           900:                return;
        !           901: 
        !           902:        for (i = 1, trigger = g_edicts + 1; i < globals.num_edicts; i++, trigger++)
        !           903:        {
        !           904:                if (!trigger->inuse)
        !           905:                        continue;
        !           906:                if (trigger->touch == Touch_Plat_Center2)
        !           907:                {
        !           908:                        if (trigger->enemy == ent)
        !           909:                        {
        !           910: //                             Touch_Plat_Center2 (trigger, activator, NULL, NULL);
        !           911:                                plat2_operate (trigger, activator);
        !           912:                                return;
        !           913:                        }
        !           914:                } 
        !           915:        }
        !           916: }
        !           917: 
        !           918: void plat2_activate (edict_t *ent, edict_t *other, edict_t *activator)
        !           919: {
        !           920:        edict_t *trigger;
        !           921: 
        !           922: //     if(ent->targetname)
        !           923: //             ent->targetname[0] = 0;
        !           924: 
        !           925:        ent->use = Use_Plat2;
        !           926: 
        !           927:        trigger = plat_spawn_inside_trigger (ent);      // the "start moving" trigger   
        !           928: 
        !           929:        trigger->maxs[0]+=10;
        !           930:        trigger->maxs[1]+=10;
        !           931:        trigger->mins[0]-=10;
        !           932:        trigger->mins[1]-=10;
        !           933: 
        !           934:        gi.linkentity (trigger);
        !           935:        
        !           936:        trigger->touch = Touch_Plat_Center2;            // Override trigger touch function
        !           937: 
        !           938:        plat2_go_down(ent);
        !           939: }
        !           940: 
        !           941: /*QUAKED func_plat2 (0 .5 .8) ? PLAT_LOW_TRIGGER PLAT2_TOGGLE PLAT2_TOP PLAT2_TRIGGER_TOP PLAT2_TRIGGER_BOTTOM BOX_LIFT
        !           942: speed  default 150
        !           943: 
        !           944: PLAT_LOW_TRIGGER - creates a short trigger field at the bottom
        !           945: PLAT2_TOGGLE - plat will not return to default position.
        !           946: PLAT2_TOP - plat's default position will the the top.
        !           947: PLAT2_TRIGGER_TOP - plat will trigger it's targets each time it hits top
        !           948: PLAT2_TRIGGER_BOTTOM - plat will trigger it's targets each time it hits bottom
        !           949: BOX_LIFT - this indicates that the lift is a box, rather than just a platform
        !           950: 
        !           951: Plats are always drawn in the extended position, so they will light correctly.
        !           952: 
        !           953: 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.
        !           954: 
        !           955: "speed"        overrides default 200.
        !           956: "accel" overrides default 500
        !           957: "lip"  no default
        !           958: 
        !           959: If the "height" key is set, that will determine the amount the plat moves, instead of being implicitly determoveinfoned by the model's height.
        !           960: 
        !           961: */
        !           962: void SP_func_plat2 (edict_t *ent)
        !           963: {
        !           964:        edict_t *trigger;
        !           965: 
        !           966:        VectorClear (ent->s.angles);
        !           967:        ent->solid = SOLID_BSP;
        !           968:        ent->movetype = MOVETYPE_PUSH;
        !           969: 
        !           970:        gi.setmodel (ent, ent->model);
        !           971: 
        !           972:        ent->blocked = plat2_blocked;
        !           973: 
        !           974:        if (!ent->speed)
        !           975:                ent->speed = 20;
        !           976:        else
        !           977:                ent->speed *= 0.1;
        !           978: 
        !           979:        if (!ent->accel)
        !           980:                ent->accel = 5;
        !           981:        else
        !           982:                ent->accel *= 0.1;
        !           983: 
        !           984:        if (!ent->decel)
        !           985:                ent->decel = 5;
        !           986:        else
        !           987:                ent->decel *= 0.1;
        !           988: 
        !           989:        if (deathmatch->value)
        !           990:        {
        !           991:                ent->speed *= 2;
        !           992:                ent->accel *= 2;
        !           993:                ent->decel *= 2;
        !           994:        }
        !           995: 
        !           996: 
        !           997:        //PMM Added to kill things it's being blocked by 
        !           998:        if (!ent->dmg)
        !           999:                ent->dmg = 2;
        !          1000: 
        !          1001: //     if (!st.lip)
        !          1002: //             st.lip = 8;
        !          1003: 
        !          1004:        // pos1 is the top position, pos2 is the bottom
        !          1005:        VectorCopy (ent->s.origin, ent->pos1);
        !          1006:        VectorCopy (ent->s.origin, ent->pos2);
        !          1007: 
        !          1008:        if (st.height)
        !          1009:                ent->pos2[2] -= (st.height - st.lip);
        !          1010:        else
        !          1011:                ent->pos2[2] -= (ent->maxs[2] - ent->mins[2]) - st.lip;
        !          1012: 
        !          1013:        ent->moveinfo.state = STATE_TOP;
        !          1014: 
        !          1015:        if(ent->targetname)
        !          1016:        {
        !          1017:                ent->use = plat2_activate;
        !          1018:        }
        !          1019:        else
        !          1020:        {
        !          1021:                ent->use = Use_Plat2;
        !          1022: 
        !          1023:                trigger = plat_spawn_inside_trigger (ent);      // the "start moving" trigger   
        !          1024: 
        !          1025:                // PGM - debugging??
        !          1026:                trigger->maxs[0]+=10;
        !          1027:                trigger->maxs[1]+=10;
        !          1028:                trigger->mins[0]-=10;
        !          1029:                trigger->mins[1]-=10;
        !          1030: 
        !          1031:                gi.linkentity (trigger);
        !          1032: 
        !          1033:                trigger->touch = Touch_Plat_Center2;            // Override trigger touch function
        !          1034: 
        !          1035:                if(!(ent->spawnflags & PLAT2_TOP))
        !          1036:                {
        !          1037:                        VectorCopy (ent->pos2, ent->s.origin);
        !          1038:                        ent->moveinfo.state = STATE_BOTTOM;
        !          1039:                }       
        !          1040:        }
        !          1041: 
        !          1042:        gi.linkentity (ent);
        !          1043: 
        !          1044:        ent->moveinfo.speed = ent->speed;
        !          1045:        ent->moveinfo.accel = ent->accel;
        !          1046:        ent->moveinfo.decel = ent->decel;
        !          1047:        ent->moveinfo.wait = ent->wait;
        !          1048:        VectorCopy (ent->pos1, ent->moveinfo.start_origin);
        !          1049:        VectorCopy (ent->s.angles, ent->moveinfo.start_angles);
        !          1050:        VectorCopy (ent->pos2, ent->moveinfo.end_origin);
        !          1051:        VectorCopy (ent->s.angles, ent->moveinfo.end_angles);
        !          1052: 
        !          1053:        ent->moveinfo.sound_start = gi.soundindex ("plats/pt1_strt.wav");
        !          1054:        ent->moveinfo.sound_middle = gi.soundindex ("plats/pt1_mid.wav");
        !          1055:        ent->moveinfo.sound_end = gi.soundindex ("plats/pt1_end.wav");
        !          1056: }
        !          1057: 
        !          1058: 
        !          1059: //====================================================================
        !          1060: 
        !          1061: /*QUAKED func_rotating (0 .5 .8) ? START_ON REVERSE X_AXIS Y_AXIS TOUCH_PAIN STOP ANIMATED ANIMATED_FAST EAST MED HARD DM COOP ACCEL
        !          1062: You need to have an origin brush as part of this entity.  The center of that brush will be
        !          1063: the point around which it is rotated. It will rotate around the Z axis by default.  You can
        !          1064: check either the X_AXIS or Y_AXIS box to change that.
        !          1065: 
        !          1066: func_rotating will use it's targets when it stops and starts.
        !          1067: 
        !          1068: "speed" determines how fast it moves; default value is 100.
        !          1069: "dmg"  damage to inflict when blocked (2 default)
        !          1070: "accel" if specified, is how much the rotation speed will increase per .1sec.
        !          1071: 
        !          1072: REVERSE will cause the it to rotate in the opposite direction.
        !          1073: STOP mean it will stop moving instead of pushing entities
        !          1074: ACCEL means it will accelerate to it's final speed and decelerate when shutting down.
        !          1075: */
        !          1076: 
        !          1077: //============
        !          1078: //PGM
        !          1079: void rotating_accel (edict_t *self)
        !          1080: {
        !          1081:        float   current_speed;
        !          1082: 
        !          1083:        current_speed = VectorLength (self->avelocity);
        !          1084:        if(current_speed >= (self->speed - self->accel))                // done
        !          1085:        {
        !          1086:                VectorScale (self->movedir, self->speed, self->avelocity);
        !          1087:                G_UseTargets (self, self);
        !          1088:        }
        !          1089:        else
        !          1090:        {
        !          1091:                current_speed += self->accel;
        !          1092:                VectorScale (self->movedir, current_speed, self->avelocity);
        !          1093:                self->think = rotating_accel;
        !          1094:                self->nextthink = level.time + FRAMETIME;
        !          1095:        }
        !          1096: }
        !          1097: 
        !          1098: void rotating_decel (edict_t *self)
        !          1099: {
        !          1100:        float   current_speed;
        !          1101: 
        !          1102:        current_speed = VectorLength (self->avelocity);
        !          1103:        if(current_speed <= self->decel)                // done
        !          1104:        {
        !          1105:                VectorClear (self->avelocity);
        !          1106:                G_UseTargets (self, self);
        !          1107:                self->touch = NULL;
        !          1108:        }
        !          1109:        else
        !          1110:        {
        !          1111:                current_speed -= self->decel;
        !          1112:                VectorScale (self->movedir, current_speed, self->avelocity);
        !          1113:                self->think = rotating_decel;
        !          1114:                self->nextthink = level.time + FRAMETIME;
        !          1115:        }
        !          1116: }
        !          1117: //PGM
        !          1118: //============
        !          1119: 
        !          1120: 
        !          1121: void rotating_blocked (edict_t *self, edict_t *other)
        !          1122: {
        !          1123:        T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
        !          1124: }
        !          1125: 
        !          1126: void rotating_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
        !          1127: {
        !          1128:        if (self->avelocity[0] || self->avelocity[1] || self->avelocity[2])
        !          1129:                T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
        !          1130: }
        !          1131: 
        !          1132: void rotating_use (edict_t *self, edict_t *other, edict_t *activator)
        !          1133: {
        !          1134:        if (!VectorCompare (self->avelocity, vec3_origin))
        !          1135:        {
        !          1136:                self->s.sound = 0;
        !          1137: //PGM
        !          1138:                if(self->spawnflags & 8192)     // Decelerate
        !          1139:                        rotating_decel (self);
        !          1140:                else
        !          1141:                {
        !          1142:                        VectorClear (self->avelocity);
        !          1143:                        G_UseTargets (self, self);
        !          1144:                        self->touch = NULL;
        !          1145:                }
        !          1146: //PGM
        !          1147:        }
        !          1148:        else
        !          1149:        {
        !          1150:                self->s.sound = self->moveinfo.sound_middle;
        !          1151: //PGM
        !          1152:                if(self->spawnflags & 8192)     // accelerate
        !          1153:                        rotating_accel (self);
        !          1154:                else
        !          1155:                {
        !          1156:                        VectorScale (self->movedir, self->speed, self->avelocity);
        !          1157:                        G_UseTargets (self, self);
        !          1158:                }
        !          1159:                if (self->spawnflags & 16)
        !          1160:                        self->touch = rotating_touch;
        !          1161: //PGM
        !          1162:        }
        !          1163: }
        !          1164: 
        !          1165: void SP_func_rotating (edict_t *ent)
        !          1166: {
        !          1167:        ent->solid = SOLID_BSP;
        !          1168:        if (ent->spawnflags & 32)
        !          1169:                ent->movetype = MOVETYPE_STOP;
        !          1170:        else
        !          1171:                ent->movetype = MOVETYPE_PUSH;
        !          1172: 
        !          1173:        // set the axis of rotation
        !          1174:        VectorClear(ent->movedir);
        !          1175:        if (ent->spawnflags & 4)
        !          1176:                ent->movedir[2] = 1.0;
        !          1177:        else if (ent->spawnflags & 8)
        !          1178:                ent->movedir[0] = 1.0;
        !          1179:        else // Z_AXIS
        !          1180:                ent->movedir[1] = 1.0;
        !          1181: 
        !          1182:        // check for reverse rotation
        !          1183:        if (ent->spawnflags & 2)
        !          1184:                VectorNegate (ent->movedir, ent->movedir);
        !          1185: 
        !          1186:        if (!ent->speed)
        !          1187:                ent->speed = 100;
        !          1188:        if (!ent->dmg)
        !          1189:                ent->dmg = 2;
        !          1190: 
        !          1191: //     ent->moveinfo.sound_middle = "doors/hydro1.wav";
        !          1192: 
        !          1193:        ent->use = rotating_use;
        !          1194:        if (ent->dmg)
        !          1195:                ent->blocked = rotating_blocked;
        !          1196: 
        !          1197:        if (ent->spawnflags & 1)
        !          1198:                ent->use (ent, NULL, NULL);
        !          1199: 
        !          1200:        if (ent->spawnflags & 64)
        !          1201:                ent->s.effects |= EF_ANIM_ALL;
        !          1202:        if (ent->spawnflags & 128)
        !          1203:                ent->s.effects |= EF_ANIM_ALLFAST;
        !          1204: 
        !          1205: //PGM
        !          1206:        if(ent->spawnflags & 8192)      // Accelerate / Decelerate
        !          1207:        {
        !          1208:                if(!ent->accel)
        !          1209:                        ent->accel = 1;
        !          1210:                else if (ent->accel > ent->speed)
        !          1211:                        ent->accel = ent->speed;
        !          1212: 
        !          1213:                if(!ent->decel)
        !          1214:                        ent->decel = 1;
        !          1215:                else if (ent->decel > ent->speed)
        !          1216:                        ent->decel = ent->speed;
        !          1217:        }
        !          1218: //PGM
        !          1219: 
        !          1220:        gi.setmodel (ent, ent->model);
        !          1221:        gi.linkentity (ent);
        !          1222: }
        !          1223: 
        !          1224: /*
        !          1225: ======================================================================
        !          1226: 
        !          1227: BUTTONS
        !          1228: 
        !          1229: ======================================================================
        !          1230: */
        !          1231: 
        !          1232: /*QUAKED func_button (0 .5 .8) ?
        !          1233: 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.
        !          1234: 
        !          1235: "angle"                determines the opening direction
        !          1236: "target"       all entities with a matching targetname will be used
        !          1237: "speed"                override the default 40 speed
        !          1238: "wait"         override the default 1 second wait (-1 = never return)
        !          1239: "lip"          override the default 4 pixel lip remaining at end of move
        !          1240: "health"       if set, the button must be killed instead of touched
        !          1241: "sounds"
        !          1242: 1) silent
        !          1243: 2) steam metal
        !          1244: 3) wooden clunk
        !          1245: 4) metallic click
        !          1246: 5) in-out
        !          1247: */
        !          1248: 
        !          1249: void button_done (edict_t *self)
        !          1250: {
        !          1251:        self->moveinfo.state = STATE_BOTTOM;
        !          1252:        self->s.effects &= ~EF_ANIM23;
        !          1253:        self->s.effects |= EF_ANIM01;
        !          1254: }
        !          1255: 
        !          1256: void button_return (edict_t *self)
        !          1257: {
        !          1258:        self->moveinfo.state = STATE_DOWN;
        !          1259: 
        !          1260:        Move_Calc (self, self->moveinfo.start_origin, button_done);
        !          1261: 
        !          1262:        self->s.frame = 0;
        !          1263: 
        !          1264:        if (self->health)
        !          1265:                self->takedamage = DAMAGE_YES;
        !          1266: }
        !          1267: 
        !          1268: void button_wait (edict_t *self)
        !          1269: {
        !          1270:        self->moveinfo.state = STATE_TOP;
        !          1271:        self->s.effects &= ~EF_ANIM01;
        !          1272:        self->s.effects |= EF_ANIM23;
        !          1273: 
        !          1274:        G_UseTargets (self, self->activator);
        !          1275:        self->s.frame = 1;
        !          1276:        if (self->moveinfo.wait >= 0)
        !          1277:        {
        !          1278:                self->nextthink = level.time + self->moveinfo.wait;
        !          1279:                self->think = button_return;
        !          1280:        }
        !          1281: }
        !          1282: 
        !          1283: void button_fire (edict_t *self)
        !          1284: {
        !          1285:        if (self->moveinfo.state == STATE_UP || self->moveinfo.state == STATE_TOP)
        !          1286:                return;
        !          1287: 
        !          1288:        self->moveinfo.state = STATE_UP;
        !          1289:        if (self->moveinfo.sound_start && !(self->flags & FL_TEAMSLAVE))
        !          1290:                gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
        !          1291:        Move_Calc (self, self->moveinfo.end_origin, button_wait);
        !          1292: }
        !          1293: 
        !          1294: void button_use (edict_t *self, edict_t *other, edict_t *activator)
        !          1295: {
        !          1296:        self->activator = activator;
        !          1297:        button_fire (self);
        !          1298: }
        !          1299: 
        !          1300: void button_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
        !          1301: {
        !          1302:        if (!other->client)
        !          1303:                return;
        !          1304: 
        !          1305:        if (other->health <= 0)
        !          1306:                return;
        !          1307: 
        !          1308:        self->activator = other;
        !          1309:        button_fire (self);
        !          1310: }
        !          1311: 
        !          1312: void button_killed (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
        !          1313: {
        !          1314:        self->activator = attacker;
        !          1315:        self->health = self->max_health;
        !          1316:        self->takedamage = DAMAGE_NO;
        !          1317:        button_fire (self);
        !          1318: }
        !          1319: 
        !          1320: void SP_func_button (edict_t *ent)
        !          1321: {
        !          1322:        vec3_t  abs_movedir;
        !          1323:        float   dist;
        !          1324: 
        !          1325:        G_SetMovedir (ent->s.angles, ent->movedir);
        !          1326:        ent->movetype = MOVETYPE_STOP;
        !          1327:        ent->solid = SOLID_BSP;
        !          1328:        gi.setmodel (ent, ent->model);
        !          1329: 
        !          1330:        if (ent->sounds != 1)
        !          1331:                ent->moveinfo.sound_start = gi.soundindex ("switches/butn2.wav");
        !          1332:        
        !          1333:        if (!ent->speed)
        !          1334:                ent->speed = 40;
        !          1335:        if (!ent->accel)
        !          1336:                ent->accel = ent->speed;
        !          1337:        if (!ent->decel)
        !          1338:                ent->decel = ent->speed;
        !          1339: 
        !          1340:        if (!ent->wait)
        !          1341:                ent->wait = 3;
        !          1342:        if (!st.lip)
        !          1343:                st.lip = 4;
        !          1344: 
        !          1345:        VectorCopy (ent->s.origin, ent->pos1);
        !          1346:        abs_movedir[0] = fabs(ent->movedir[0]);
        !          1347:        abs_movedir[1] = fabs(ent->movedir[1]);
        !          1348:        abs_movedir[2] = fabs(ent->movedir[2]);
        !          1349:        dist = abs_movedir[0] * ent->size[0] + abs_movedir[1] * ent->size[1] + abs_movedir[2] * ent->size[2] - st.lip;
        !          1350:        VectorMA (ent->pos1, dist, ent->movedir, ent->pos2);
        !          1351: 
        !          1352:        ent->use = button_use;
        !          1353:        ent->s.effects |= EF_ANIM01;
        !          1354: 
        !          1355:        if (ent->health)
        !          1356:        {
        !          1357:                ent->max_health = ent->health;
        !          1358:                ent->die = button_killed;
        !          1359:                ent->takedamage = DAMAGE_YES;
        !          1360:        }
        !          1361:        else if (! ent->targetname)
        !          1362:                ent->touch = button_touch;
        !          1363: 
        !          1364:        ent->moveinfo.state = STATE_BOTTOM;
        !          1365: 
        !          1366:        ent->moveinfo.speed = ent->speed;
        !          1367:        ent->moveinfo.accel = ent->accel;
        !          1368:        ent->moveinfo.decel = ent->decel;
        !          1369:        ent->moveinfo.wait = ent->wait;
        !          1370:        VectorCopy (ent->pos1, ent->moveinfo.start_origin);
        !          1371:        VectorCopy (ent->s.angles, ent->moveinfo.start_angles);
        !          1372:        VectorCopy (ent->pos2, ent->moveinfo.end_origin);
        !          1373:        VectorCopy (ent->s.angles, ent->moveinfo.end_angles);
        !          1374: 
        !          1375:        gi.linkentity (ent);
        !          1376: }
        !          1377: 
        !          1378: /*
        !          1379: ======================================================================
        !          1380: 
        !          1381: DOORS
        !          1382: 
        !          1383:   spawn a trigger surrounding the entire team unless it is
        !          1384:   already targeted by another
        !          1385: 
        !          1386: ======================================================================
        !          1387: */
        !          1388: 
        !          1389: /*QUAKED func_door (0 .5 .8) ? START_OPEN x CRUSHER NOMONSTER ANIMATED TOGGLE ANIMATED_FAST
        !          1390: TOGGLE         wait in both the start and end states for a trigger event.
        !          1391: 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).
        !          1392: NOMONSTER      monsters will not trigger this door
        !          1393: 
        !          1394: "message"      is printed when the door is touched if it is a trigger door and it hasn't been fired yet
        !          1395: "angle"                determines the opening direction
        !          1396: "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
        !          1397: "health"       if set, door must be shot open
        !          1398: "speed"                movement speed (100 default)
        !          1399: "wait"         wait before returning (3 default, -1 = never return)
        !          1400: "lip"          lip remaining at end of move (8 default)
        !          1401: "dmg"          damage to inflict when blocked (2 default)
        !          1402: "sounds"
        !          1403: 1)     silent
        !          1404: 2)     light
        !          1405: 3)     medium
        !          1406: 4)     heavy
        !          1407: */
        !          1408: 
        !          1409: void door_use_areaportals (edict_t *self, qboolean open)
        !          1410: {
        !          1411:        edict_t *t = NULL;
        !          1412: 
        !          1413:        if (!self->target)
        !          1414:                return;
        !          1415: 
        !          1416:        while ((t = G_Find (t, FOFS(targetname), self->target)))
        !          1417:        {
        !          1418:                if (Q_stricmp(t->classname, "func_areaportal") == 0)
        !          1419:                {
        !          1420:                        gi.SetAreaPortalState (t->style, open);
        !          1421:                }
        !          1422:        }
        !          1423: }
        !          1424: 
        !          1425: void door_go_down (edict_t *self);
        !          1426: 
        !          1427: void door_hit_top (edict_t *self)
        !          1428: {
        !          1429:        if (!(self->flags & FL_TEAMSLAVE))
        !          1430:        {
        !          1431:                if (self->moveinfo.sound_end)
        !          1432:                        gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_end, 1, ATTN_STATIC, 0);
        !          1433:                self->s.sound = 0;
        !          1434:        }
        !          1435:        self->moveinfo.state = STATE_TOP;
        !          1436:        if (self->spawnflags & DOOR_TOGGLE)
        !          1437:                return;
        !          1438:        if (self->moveinfo.wait >= 0)
        !          1439:        {
        !          1440:                self->think = door_go_down;
        !          1441:                self->nextthink = level.time + self->moveinfo.wait;
        !          1442:        }
        !          1443: }
        !          1444: 
        !          1445: void door_hit_bottom (edict_t *self)
        !          1446: {
        !          1447:        if (!(self->flags & FL_TEAMSLAVE))
        !          1448:        {
        !          1449:                if (self->moveinfo.sound_end)
        !          1450:                        gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_end, 1, ATTN_STATIC, 0);
        !          1451:                self->s.sound = 0;
        !          1452:        }
        !          1453:        self->moveinfo.state = STATE_BOTTOM;
        !          1454:        door_use_areaportals (self, false);
        !          1455: }
        !          1456: 
        !          1457: void door_go_down (edict_t *self)
        !          1458: {
        !          1459:        if (!(self->flags & FL_TEAMSLAVE))
        !          1460:        {
        !          1461:                if (self->moveinfo.sound_start)
        !          1462:                        gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
        !          1463:                self->s.sound = self->moveinfo.sound_middle;
        !          1464:        }
        !          1465:        if (self->max_health)
        !          1466:        {
        !          1467:                self->takedamage = DAMAGE_YES;
        !          1468:                self->health = self->max_health;
        !          1469:        }
        !          1470:        
        !          1471:        self->moveinfo.state = STATE_DOWN;
        !          1472:        if (strcmp(self->classname, "func_door") == 0)
        !          1473:                Move_Calc (self, self->moveinfo.start_origin, door_hit_bottom);
        !          1474:        else if (strcmp(self->classname, "func_door_rotating") == 0)
        !          1475:                AngleMove_Calc (self, door_hit_bottom);
        !          1476: }
        !          1477: 
        !          1478: void door_go_up (edict_t *self, edict_t *activator)
        !          1479: {
        !          1480:        if (self->moveinfo.state == STATE_UP)
        !          1481:                return;         // already going up
        !          1482: 
        !          1483:        if (self->moveinfo.state == STATE_TOP)
        !          1484:        {       // reset top wait time
        !          1485:                if (self->moveinfo.wait >= 0)
        !          1486:                        self->nextthink = level.time + self->moveinfo.wait;
        !          1487:                return;
        !          1488:        }
        !          1489:        
        !          1490:        if (!(self->flags & FL_TEAMSLAVE))
        !          1491:        {
        !          1492:                if (self->moveinfo.sound_start)
        !          1493:                        gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
        !          1494:                self->s.sound = self->moveinfo.sound_middle;
        !          1495:        }
        !          1496:        self->moveinfo.state = STATE_UP;
        !          1497:        if (strcmp(self->classname, "func_door") == 0)
        !          1498:                Move_Calc (self, self->moveinfo.end_origin, door_hit_top);
        !          1499:        else if (strcmp(self->classname, "func_door_rotating") == 0)
        !          1500:                AngleMove_Calc (self, door_hit_top);
        !          1501: 
        !          1502:        G_UseTargets (self, activator);
        !          1503:        door_use_areaportals (self, true);
        !          1504: }
        !          1505: 
        !          1506: //======
        !          1507: //PGM
        !          1508: void smart_water_go_up (edict_t *self)
        !          1509: {
        !          1510:        float           distance;
        !          1511:        edict_t         *lowestPlayer;
        !          1512:        edict_t         *ent;
        !          1513:        float           lowestPlayerPt;
        !          1514:        int                     i;
        !          1515: 
        !          1516:        if (self->moveinfo.state == STATE_TOP)
        !          1517:        {       // reset top wait time
        !          1518:                if (self->moveinfo.wait >= 0)
        !          1519:                        self->nextthink = level.time + self->moveinfo.wait;
        !          1520:                return;
        !          1521:        }
        !          1522: 
        !          1523:        if (self->health)
        !          1524:        {
        !          1525:                if(self->absmax[2] >= self->health)
        !          1526:                {
        !          1527:                        gi.dprintf("smart water stopping at %0.1f\n", self->absmax[2]);
        !          1528:                        VectorClear (self->velocity);
        !          1529:                        self->nextthink = 0;
        !          1530:                        self->moveinfo.state = STATE_TOP;
        !          1531:                        return;
        !          1532:                }
        !          1533:        }
        !          1534: 
        !          1535:        if (!(self->flags & FL_TEAMSLAVE))
        !          1536:        {
        !          1537:                if (self->moveinfo.sound_start)
        !          1538:                        gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
        !          1539:                self->s.sound = self->moveinfo.sound_middle;
        !          1540:        }
        !          1541: 
        !          1542:        // find the lowest player point.
        !          1543:        lowestPlayerPt = 999999;
        !          1544:        lowestPlayer = NULL;
        !          1545:        for (i=0 ; i<game.maxclients ; i++)
        !          1546:        {
        !          1547:                ent = &g_edicts[1+i];
        !          1548:                if(ent->absmin[2] < lowestPlayerPt)
        !          1549:                {
        !          1550:                        lowestPlayerPt = ent->absmin[2];
        !          1551:                        lowestPlayer = ent;
        !          1552:                }
        !          1553:        }
        !          1554: 
        !          1555:        if(!lowestPlayer)
        !          1556:        {
        !          1557:                gi.dprintf("smart water without enemy!\n");
        !          1558:                return;
        !          1559:        }
        !          1560: 
        !          1561:        distance = lowestPlayerPt - self->absmax[2];
        !          1562: 
        !          1563:        // for the calculations, make sure we intend to go up at least a little.
        !          1564:        if(distance < 20)
        !          1565:        {
        !          1566:                distance = 100;
        !          1567:                self->moveinfo.speed = 5;
        !          1568:        }
        !          1569:        else
        !          1570:                self->moveinfo.speed = distance / 20;
        !          1571: 
        !          1572:        if(self->moveinfo.speed < 5)
        !          1573:                self->moveinfo.speed = 5;
        !          1574:        else if(self->moveinfo.speed > self->speed)
        !          1575:                self->moveinfo.speed = self->speed;
        !          1576: 
        !          1577:        // FIXME - should this allow any movement other than straight up?
        !          1578:        VectorSet(self->moveinfo.dir, 0, 0, 1); 
        !          1579:        VectorScale (self->moveinfo.dir, self->moveinfo.speed, self->velocity);
        !          1580:        self->moveinfo.remaining_distance = distance;
        !          1581: 
        !          1582:        if(self->moveinfo.state != STATE_UP)
        !          1583:        {
        !          1584:                G_UseTargets (self, lowestPlayer);
        !          1585:                door_use_areaportals (self, true);
        !          1586:                self->moveinfo.state = STATE_UP;
        !          1587:        }
        !          1588: 
        !          1589:        self->think = smart_water_go_up;
        !          1590:        self->nextthink = level.time + FRAMETIME;
        !          1591: }
        !          1592: //PGM
        !          1593: //======
        !          1594: 
        !          1595: void door_use (edict_t *self, edict_t *other, edict_t *activator)
        !          1596: {
        !          1597:        edict_t *ent;
        !          1598:        vec3_t  center;                 //PGM
        !          1599: 
        !          1600:        if (self->flags & FL_TEAMSLAVE)
        !          1601:                return;
        !          1602: 
        !          1603:        if (self->spawnflags & DOOR_TOGGLE)
        !          1604:        {
        !          1605:                if (self->moveinfo.state == STATE_UP || self->moveinfo.state == STATE_TOP)
        !          1606:                {
        !          1607:                        // trigger all paired doors
        !          1608:                        for (ent = self ; ent ; ent = ent->teamchain)
        !          1609:                        {
        !          1610:                                ent->message = NULL;
        !          1611:                                ent->touch = NULL;
        !          1612:                                door_go_down (ent);
        !          1613:                        }
        !          1614:                        return;
        !          1615:                }
        !          1616:        }
        !          1617: 
        !          1618: //PGM
        !          1619:        // smart water is different
        !          1620:        VectorAdd(self->mins, self->maxs, center);
        !          1621:        VectorScale(center, 0.5, center);
        !          1622:        if ((gi.pointcontents (center) & MASK_WATER) && self->spawnflags & 2)
        !          1623:        {
        !          1624:                self->message = NULL;
        !          1625:                self->touch = NULL;
        !          1626:                self->enemy = activator;
        !          1627:                smart_water_go_up (self);
        !          1628:                return;
        !          1629:        }
        !          1630: //PGM
        !          1631: 
        !          1632:        // trigger all paired doors
        !          1633:        for (ent = self ; ent ; ent = ent->teamchain)
        !          1634:        {
        !          1635:                ent->message = NULL;
        !          1636:                ent->touch = NULL;
        !          1637:                door_go_up (ent, activator);
        !          1638:        }
        !          1639: };
        !          1640: 
        !          1641: void Touch_DoorTrigger (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
        !          1642: {
        !          1643:        if (other->health <= 0)
        !          1644:                return;
        !          1645: 
        !          1646:        if (!(other->svflags & SVF_MONSTER) && (!other->client))
        !          1647:                return;
        !          1648: 
        !          1649:        if ((self->owner->spawnflags & DOOR_NOMONSTER) && (other->svflags & SVF_MONSTER))
        !          1650:                return;
        !          1651: 
        !          1652:        if (level.time < self->touch_debounce_time)
        !          1653:                return;
        !          1654:        self->touch_debounce_time = level.time + 1.0;
        !          1655: 
        !          1656:        door_use (self->owner, other, other);
        !          1657: }
        !          1658: 
        !          1659: void Think_CalcMoveSpeed (edict_t *self)
        !          1660: {
        !          1661:        edict_t *ent;
        !          1662:        float   min;
        !          1663:        float   time;
        !          1664:        float   newspeed;
        !          1665:        float   ratio;
        !          1666:        float   dist;
        !          1667: 
        !          1668:        if (self->flags & FL_TEAMSLAVE)
        !          1669:                return;         // only the team master does this
        !          1670: 
        !          1671:        // find the smallest distance any member of the team will be moving
        !          1672:        min = fabs(self->moveinfo.distance);
        !          1673:        for (ent = self->teamchain; ent; ent = ent->teamchain)
        !          1674:        {
        !          1675:                dist = fabs(ent->moveinfo.distance);
        !          1676:                if (dist < min)
        !          1677:                        min = dist;
        !          1678:        }
        !          1679: 
        !          1680:        time = min / self->moveinfo.speed;
        !          1681: 
        !          1682:        // adjust speeds so they will all complete at the same time
        !          1683:        for (ent = self; ent; ent = ent->teamchain)
        !          1684:        {
        !          1685:                newspeed = fabs(ent->moveinfo.distance) / time;
        !          1686:                ratio = newspeed / ent->moveinfo.speed;
        !          1687:                if (ent->moveinfo.accel == ent->moveinfo.speed)
        !          1688:                        ent->moveinfo.accel = newspeed;
        !          1689:                else
        !          1690:                        ent->moveinfo.accel *= ratio;
        !          1691:                if (ent->moveinfo.decel == ent->moveinfo.speed)
        !          1692:                        ent->moveinfo.decel = newspeed;
        !          1693:                else
        !          1694:                        ent->moveinfo.decel *= ratio;
        !          1695:                ent->moveinfo.speed = newspeed;
        !          1696:        }
        !          1697: }
        !          1698: 
        !          1699: void Think_SpawnDoorTrigger (edict_t *ent)
        !          1700: {
        !          1701:        edict_t         *other;
        !          1702:        vec3_t          mins, maxs;
        !          1703: 
        !          1704:        if (ent->flags & FL_TEAMSLAVE)
        !          1705:                return;         // only the team leader spawns a trigger
        !          1706: 
        !          1707:        VectorCopy (ent->absmin, mins);
        !          1708:        VectorCopy (ent->absmax, maxs);
        !          1709: 
        !          1710:        for (other = ent->teamchain ; other ; other=other->teamchain)
        !          1711:        {
        !          1712:                AddPointToBounds (other->absmin, mins, maxs);
        !          1713:                AddPointToBounds (other->absmax, mins, maxs);
        !          1714:        }
        !          1715: 
        !          1716:        // expand 
        !          1717:        mins[0] -= 60;
        !          1718:        mins[1] -= 60;
        !          1719:        maxs[0] += 60;
        !          1720:        maxs[1] += 60;
        !          1721: 
        !          1722:        other = G_Spawn ();
        !          1723:        VectorCopy (mins, other->mins);
        !          1724:        VectorCopy (maxs, other->maxs);
        !          1725:        other->owner = ent;
        !          1726:        other->solid = SOLID_TRIGGER;
        !          1727:        other->movetype = MOVETYPE_NONE;
        !          1728:        other->touch = Touch_DoorTrigger;
        !          1729:        gi.linkentity (other);
        !          1730: 
        !          1731:        if (ent->spawnflags & DOOR_START_OPEN)
        !          1732:                door_use_areaportals (ent, true);
        !          1733: 
        !          1734:        Think_CalcMoveSpeed (ent);
        !          1735: }
        !          1736: 
        !          1737: void door_blocked  (edict_t *self, edict_t *other)
        !          1738: {
        !          1739:        edict_t *ent;
        !          1740: 
        !          1741:        if (!(other->svflags & SVF_MONSTER) && (!other->client) )
        !          1742:        {
        !          1743:                // give it a chance to go away on it's own terms (like gibs)
        !          1744:                T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
        !          1745:                // if it's still there, nuke it
        !          1746:                if (other && other->inuse)
        !          1747:                        BecomeExplosion1 (other);
        !          1748:                return;
        !          1749:        }
        !          1750: 
        !          1751:        T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
        !          1752: 
        !          1753:        if (self->spawnflags & DOOR_CRUSHER)
        !          1754:                return;
        !          1755: 
        !          1756: 
        !          1757: // if a door has a negative wait, it would never come back if blocked,
        !          1758: // so let it just squash the object to death real fast
        !          1759:        if (self->moveinfo.wait >= 0)
        !          1760:        {
        !          1761:                if (self->moveinfo.state == STATE_DOWN)
        !          1762:                {
        !          1763:                        for (ent = self->teammaster ; ent ; ent = ent->teamchain)
        !          1764:                                door_go_up (ent, ent->activator);
        !          1765:                }
        !          1766:                else
        !          1767:                {
        !          1768:                        for (ent = self->teammaster ; ent ; ent = ent->teamchain)
        !          1769:                                door_go_down (ent);
        !          1770:                }
        !          1771:        }
        !          1772: }
        !          1773: 
        !          1774: void door_killed (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
        !          1775: {
        !          1776:        edict_t *ent;
        !          1777: 
        !          1778:        for (ent = self->teammaster ; ent ; ent = ent->teamchain)
        !          1779:        {
        !          1780:                ent->health = ent->max_health;
        !          1781:                ent->takedamage = DAMAGE_NO;
        !          1782:        }
        !          1783:        door_use (self->teammaster, attacker, attacker);
        !          1784: }
        !          1785: 
        !          1786: void door_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
        !          1787: {
        !          1788:        if (!other->client)
        !          1789:                return;
        !          1790: 
        !          1791:        if (level.time < self->touch_debounce_time)
        !          1792:                return;
        !          1793:        self->touch_debounce_time = level.time + 5.0;
        !          1794: 
        !          1795:        gi.centerprintf (other, "%s", self->message);
        !          1796:        gi.sound (other, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
        !          1797: }
        !          1798: 
        !          1799: void SP_func_door (edict_t *ent)
        !          1800: {
        !          1801:        vec3_t  abs_movedir;
        !          1802: 
        !          1803:        if (ent->sounds != 1)
        !          1804:        {
        !          1805:                ent->moveinfo.sound_start = gi.soundindex  ("doors/dr1_strt.wav");
        !          1806:                ent->moveinfo.sound_middle = gi.soundindex  ("doors/dr1_mid.wav");
        !          1807:                ent->moveinfo.sound_end = gi.soundindex  ("doors/dr1_end.wav");
        !          1808:        }
        !          1809: 
        !          1810:        G_SetMovedir (ent->s.angles, ent->movedir);
        !          1811:        ent->movetype = MOVETYPE_PUSH;
        !          1812:        ent->solid = SOLID_BSP;
        !          1813:        gi.setmodel (ent, ent->model);
        !          1814: 
        !          1815:        ent->blocked = door_blocked;
        !          1816:        ent->use = door_use;
        !          1817:        
        !          1818:        if (!ent->speed)
        !          1819:                ent->speed = 100;
        !          1820:        if (deathmatch->value)
        !          1821:                ent->speed *= 2;
        !          1822: 
        !          1823:        if (!ent->accel)
        !          1824:                ent->accel = ent->speed;
        !          1825:        if (!ent->decel)
        !          1826:                ent->decel = ent->speed;
        !          1827: 
        !          1828:        if (!ent->wait)
        !          1829:                ent->wait = 3;
        !          1830:        if (!st.lip)
        !          1831:                st.lip = 8;
        !          1832:        if (!ent->dmg)
        !          1833:                ent->dmg = 2;
        !          1834: 
        !          1835:        // calculate second position
        !          1836:        VectorCopy (ent->s.origin, ent->pos1);
        !          1837:        abs_movedir[0] = fabs(ent->movedir[0]);
        !          1838:        abs_movedir[1] = fabs(ent->movedir[1]);
        !          1839:        abs_movedir[2] = fabs(ent->movedir[2]);
        !          1840:        ent->moveinfo.distance = abs_movedir[0] * ent->size[0] + abs_movedir[1] * ent->size[1] + abs_movedir[2] * ent->size[2] - st.lip;
        !          1841:        VectorMA (ent->pos1, ent->moveinfo.distance, ent->movedir, ent->pos2);
        !          1842: 
        !          1843:        // if it starts open, switch the positions
        !          1844:        if (ent->spawnflags & DOOR_START_OPEN)
        !          1845:        {
        !          1846:                VectorCopy (ent->pos2, ent->s.origin);
        !          1847:                VectorCopy (ent->pos1, ent->pos2);
        !          1848:                VectorCopy (ent->s.origin, ent->pos1);
        !          1849:        }
        !          1850: 
        !          1851:        ent->moveinfo.state = STATE_BOTTOM;
        !          1852: 
        !          1853:        if (ent->health)
        !          1854:        {
        !          1855:                ent->takedamage = DAMAGE_YES;
        !          1856:                ent->die = door_killed;
        !          1857:                ent->max_health = ent->health;
        !          1858:        }
        !          1859:        else if (ent->targetname && ent->message)
        !          1860:        {
        !          1861:                gi.soundindex ("misc/talk.wav");
        !          1862:                ent->touch = door_touch;
        !          1863:        }
        !          1864:        
        !          1865:        ent->moveinfo.speed = ent->speed;
        !          1866:        ent->moveinfo.accel = ent->accel;
        !          1867:        ent->moveinfo.decel = ent->decel;
        !          1868:        ent->moveinfo.wait = ent->wait;
        !          1869:        VectorCopy (ent->pos1, ent->moveinfo.start_origin);
        !          1870:        VectorCopy (ent->s.angles, ent->moveinfo.start_angles);
        !          1871:        VectorCopy (ent->pos2, ent->moveinfo.end_origin);
        !          1872:        VectorCopy (ent->s.angles, ent->moveinfo.end_angles);
        !          1873: 
        !          1874:        if (ent->spawnflags & 16)
        !          1875:                ent->s.effects |= EF_ANIM_ALL;
        !          1876:        if (ent->spawnflags & 64)
        !          1877:                ent->s.effects |= EF_ANIM_ALLFAST;
        !          1878: 
        !          1879:        // to simplify logic elsewhere, make non-teamed doors into a team of one
        !          1880:        if (!ent->team)
        !          1881:                ent->teammaster = ent;
        !          1882: 
        !          1883:        gi.linkentity (ent);
        !          1884: 
        !          1885:        ent->nextthink = level.time + FRAMETIME;
        !          1886:        if (ent->health || ent->targetname)
        !          1887:                ent->think = Think_CalcMoveSpeed;
        !          1888:        else
        !          1889:                ent->think = Think_SpawnDoorTrigger;
        !          1890: }
        !          1891: 
        !          1892: //PGM
        !          1893: void Door_Activate (edict_t *self, edict_t *other, edict_t *activator)
        !          1894: {
        !          1895:        self->use = NULL;
        !          1896:        
        !          1897:        if (self->health)
        !          1898:        {
        !          1899:                self->takedamage = DAMAGE_YES;
        !          1900:                self->die = door_killed;
        !          1901:                self->max_health = self->health;
        !          1902:        }
        !          1903: 
        !          1904:        if (self->health)
        !          1905:                self->think = Think_CalcMoveSpeed;
        !          1906:        else
        !          1907:                self->think = Think_SpawnDoorTrigger;
        !          1908:        self->nextthink = level.time + FRAMETIME;
        !          1909: 
        !          1910: }
        !          1911: //PGM
        !          1912: 
        !          1913: /*QUAKED func_door_rotating (0 .5 .8) ? START_OPEN REVERSE CRUSHER NOMONSTER ANIMATED TOGGLE X_AXIS Y_AXIS EASY MED HARD DM COOP INACTIVE
        !          1914: TOGGLE causes the door to wait in both the start and end states for a trigger event.
        !          1915: 
        !          1916: 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).
        !          1917: NOMONSTER      monsters will not trigger this door
        !          1918: 
        !          1919: You need to have an origin brush as part of this entity.  The center of that brush will be
        !          1920: the point around which it is rotated. It will rotate around the Z axis by default.  You can
        !          1921: check either the X_AXIS or Y_AXIS box to change that.
        !          1922: 
        !          1923: "distance" is how many degrees the door will be rotated.
        !          1924: "speed" determines how fast the door moves; default value is 100.
        !          1925: "accel" if specified,is how much the rotation speed will increase each .1 sec. (default: no accel)
        !          1926: 
        !          1927: REVERSE will cause the door to rotate in the opposite direction.
        !          1928: INACTIVE will cause the door to be inactive until triggered.
        !          1929: 
        !          1930: "message"      is printed when the door is touched if it is a trigger door and it hasn't been fired yet
        !          1931: "angle"                determines the opening direction
        !          1932: "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
        !          1933: "health"       if set, door must be shot open
        !          1934: "speed"                movement speed (100 default)
        !          1935: "wait"         wait before returning (3 default, -1 = never return)
        !          1936: "dmg"          damage to inflict when blocked (2 default)
        !          1937: "sounds"
        !          1938: 1)     silent
        !          1939: 2)     light
        !          1940: 3)     medium
        !          1941: 4)     heavy
        !          1942: */
        !          1943: 
        !          1944: void SP_func_door_rotating (edict_t *ent)
        !          1945: {
        !          1946:        VectorClear (ent->s.angles);
        !          1947: 
        !          1948:        // set the axis of rotation
        !          1949:        VectorClear(ent->movedir);
        !          1950:        if (ent->spawnflags & DOOR_X_AXIS)
        !          1951:                ent->movedir[2] = 1.0;
        !          1952:        else if (ent->spawnflags & DOOR_Y_AXIS)
        !          1953:                ent->movedir[0] = 1.0;
        !          1954:        else // Z_AXIS
        !          1955:                ent->movedir[1] = 1.0;
        !          1956: 
        !          1957:        // check for reverse rotation
        !          1958:        if (ent->spawnflags & DOOR_REVERSE)
        !          1959:                VectorNegate (ent->movedir, ent->movedir);
        !          1960: 
        !          1961:        if (!st.distance)
        !          1962:        {
        !          1963:                gi.dprintf("%s at %s with no distance set\n", ent->classname, vtos(ent->s.origin));
        !          1964:                st.distance = 90;
        !          1965:        }
        !          1966: 
        !          1967:        VectorCopy (ent->s.angles, ent->pos1);
        !          1968:        VectorMA (ent->s.angles, st.distance, ent->movedir, ent->pos2);
        !          1969:        ent->moveinfo.distance = st.distance;
        !          1970: 
        !          1971:        ent->movetype = MOVETYPE_PUSH;
        !          1972:        ent->solid = SOLID_BSP;
        !          1973:        gi.setmodel (ent, ent->model);
        !          1974: 
        !          1975:        ent->blocked = door_blocked;
        !          1976:        ent->use = door_use;
        !          1977: 
        !          1978:        if (!ent->speed)
        !          1979:                ent->speed = 100;
        !          1980:        if (!ent->accel)
        !          1981:                ent->accel = ent->speed;
        !          1982:        if (!ent->decel)
        !          1983:                ent->decel = ent->speed;
        !          1984: 
        !          1985:        if (!ent->wait)
        !          1986:                ent->wait = 3;
        !          1987:        if (!ent->dmg)
        !          1988:                ent->dmg = 2;
        !          1989: 
        !          1990:        if (ent->sounds != 1)
        !          1991:        {
        !          1992:                ent->moveinfo.sound_start = gi.soundindex  ("doors/dr1_strt.wav");
        !          1993:                ent->moveinfo.sound_middle = gi.soundindex  ("doors/dr1_mid.wav");
        !          1994:                ent->moveinfo.sound_end = gi.soundindex  ("doors/dr1_end.wav");
        !          1995:        }
        !          1996: 
        !          1997:        // if it starts open, switch the positions
        !          1998:        if (ent->spawnflags & DOOR_START_OPEN)
        !          1999:        {
        !          2000:                VectorCopy (ent->pos2, ent->s.angles);
        !          2001:                VectorCopy (ent->pos1, ent->pos2);
        !          2002:                VectorCopy (ent->s.angles, ent->pos1);
        !          2003:                VectorNegate (ent->movedir, ent->movedir);
        !          2004:        }
        !          2005: 
        !          2006:        if (ent->health)
        !          2007:        {
        !          2008:                ent->takedamage = DAMAGE_YES;
        !          2009:                ent->die = door_killed;
        !          2010:                ent->max_health = ent->health;
        !          2011:        }
        !          2012:        
        !          2013:        if (ent->targetname && ent->message)
        !          2014:        {
        !          2015:                gi.soundindex ("misc/talk.wav");
        !          2016:                ent->touch = door_touch;
        !          2017:        }
        !          2018: 
        !          2019:        ent->moveinfo.state = STATE_BOTTOM;
        !          2020:        ent->moveinfo.speed = ent->speed;
        !          2021:        ent->moveinfo.accel = ent->accel;
        !          2022:        ent->moveinfo.decel = ent->decel;
        !          2023:        ent->moveinfo.wait = ent->wait;
        !          2024:        VectorCopy (ent->s.origin, ent->moveinfo.start_origin);
        !          2025:        VectorCopy (ent->pos1, ent->moveinfo.start_angles);
        !          2026:        VectorCopy (ent->s.origin, ent->moveinfo.end_origin);
        !          2027:        VectorCopy (ent->pos2, ent->moveinfo.end_angles);
        !          2028: 
        !          2029:        if (ent->spawnflags & 16)
        !          2030:                ent->s.effects |= EF_ANIM_ALL;
        !          2031: 
        !          2032:        // to simplify logic elsewhere, make non-teamed doors into a team of one
        !          2033:        if (!ent->team)
        !          2034:                ent->teammaster = ent;
        !          2035: 
        !          2036:        gi.linkentity (ent);
        !          2037: 
        !          2038:        ent->nextthink = level.time + FRAMETIME;
        !          2039:        if (ent->health || ent->targetname)
        !          2040:                ent->think = Think_CalcMoveSpeed;
        !          2041:        else
        !          2042:                ent->think = Think_SpawnDoorTrigger;
        !          2043: 
        !          2044: //PGM
        !          2045:        if (ent->spawnflags & DOOR_INACTIVE)
        !          2046:        {
        !          2047:                ent->takedamage = DAMAGE_NO;
        !          2048:                ent->die = NULL;
        !          2049:                ent->think = NULL;
        !          2050:                ent->nextthink = 0;
        !          2051:                ent->use = Door_Activate;
        !          2052:        }
        !          2053: //PGM
        !          2054: }
        !          2055: 
        !          2056: 
        !          2057: /*QUAKED func_water (0 .5 .8) ? START_OPEN SMART
        !          2058: func_water is a moveable water brush.  It must be targeted to operate.  Use a non-water texture at your own risk.
        !          2059: 
        !          2060: START_OPEN causes the water to move to its destination when spawned and operate in reverse.
        !          2061: SMART causes the water to adjust its speed depending on distance to player. (speed = distance/20, min 5, max self->speed)
        !          2062: 
        !          2063: "health"       maximum height of this water brush
        !          2064: "angle"                determines the opening direction (up or down only)
        !          2065: "speed"                movement speed (25 default)
        !          2066: "wait"         wait before returning (-1 default, -1 = TOGGLE)
        !          2067: "lip"          lip remaining at end of move (0 default)
        !          2068: "sounds"       (yes, these need to be changed)
        !          2069: 0)     no sound
        !          2070: 1)     water
        !          2071: 2)     lava
        !          2072: */
        !          2073: 
        !          2074: void SP_func_water (edict_t *self)
        !          2075: {
        !          2076:        vec3_t  abs_movedir;
        !          2077: 
        !          2078:        G_SetMovedir (self->s.angles, self->movedir);
        !          2079:        self->movetype = MOVETYPE_PUSH;
        !          2080:        self->solid = SOLID_BSP;
        !          2081:        gi.setmodel (self, self->model);
        !          2082: 
        !          2083:        switch (self->sounds)
        !          2084:        {
        !          2085:                default:
        !          2086:                        break;
        !          2087: 
        !          2088:                case 1: // water
        !          2089:                        self->moveinfo.sound_start = gi.soundindex  ("world/mov_watr.wav");
        !          2090:                        self->moveinfo.sound_end = gi.soundindex  ("world/stp_watr.wav");
        !          2091:                        break;
        !          2092: 
        !          2093:                case 2: // lava
        !          2094:                        self->moveinfo.sound_start = gi.soundindex  ("world/mov_watr.wav");
        !          2095:                        self->moveinfo.sound_end = gi.soundindex  ("world/stp_watr.wav");
        !          2096:                        break;
        !          2097:        }
        !          2098: 
        !          2099:        // calculate second position
        !          2100:        VectorCopy (self->s.origin, self->pos1);
        !          2101:        abs_movedir[0] = fabs(self->movedir[0]);
        !          2102:        abs_movedir[1] = fabs(self->movedir[1]);
        !          2103:        abs_movedir[2] = fabs(self->movedir[2]);
        !          2104:        self->moveinfo.distance = abs_movedir[0] * self->size[0] + abs_movedir[1] * self->size[1] + abs_movedir[2] * self->size[2] - st.lip;
        !          2105:        VectorMA (self->pos1, self->moveinfo.distance, self->movedir, self->pos2);
        !          2106: 
        !          2107:        // if it starts open, switch the positions
        !          2108:        if (self->spawnflags & DOOR_START_OPEN)
        !          2109:        {
        !          2110:                VectorCopy (self->pos2, self->s.origin);
        !          2111:                VectorCopy (self->pos1, self->pos2);
        !          2112:                VectorCopy (self->s.origin, self->pos1);
        !          2113:        }
        !          2114: 
        !          2115:        VectorCopy (self->pos1, self->moveinfo.start_origin);
        !          2116:        VectorCopy (self->s.angles, self->moveinfo.start_angles);
        !          2117:        VectorCopy (self->pos2, self->moveinfo.end_origin);
        !          2118:        VectorCopy (self->s.angles, self->moveinfo.end_angles);
        !          2119: 
        !          2120:        self->moveinfo.state = STATE_BOTTOM;
        !          2121: 
        !          2122:        if (!self->speed)
        !          2123:                self->speed = 25;
        !          2124:        self->moveinfo.accel = self->moveinfo.decel = self->moveinfo.speed = self->speed;
        !          2125: 
        !          2126:        if (!self->wait)
        !          2127:                self->wait = -1;
        !          2128:        self->moveinfo.wait = self->wait;
        !          2129: 
        !          2130:        self->use = door_use;
        !          2131: 
        !          2132:        if (self->wait == -1)
        !          2133:                self->spawnflags |= DOOR_TOGGLE;
        !          2134: 
        !          2135:        self->classname = "func_door";
        !          2136: 
        !          2137:        gi.linkentity (self);
        !          2138: }
        !          2139: 
        !          2140: 
        !          2141: #define TRAIN_START_ON         1
        !          2142: #define TRAIN_TOGGLE           2
        !          2143: #define TRAIN_BLOCK_STOPS      4
        !          2144: 
        !          2145: /*QUAKED func_train (0 .5 .8) ? START_ON TOGGLE BLOCK_STOPS
        !          2146: Trains are moving platforms that players can ride.
        !          2147: The targets origin specifies the min point of the train at each corner.
        !          2148: The train spawns at the first target it is pointing at.
        !          2149: If the train is the target of a button or trigger, it will not begin moving until activated.
        !          2150: speed  default 100
        !          2151: dmg            default 2
        !          2152: noise  looping sound to play when the train is in motion
        !          2153: 
        !          2154: To have other entities move with the train, set all the piece's team value to the same thing. They will move in unison.
        !          2155: */
        !          2156: void train_next (edict_t *self);
        !          2157: 
        !          2158: void train_blocked (edict_t *self, edict_t *other)
        !          2159: {
        !          2160:        if (!(other->svflags & SVF_MONSTER) && (!other->client) )
        !          2161:        {
        !          2162:                // give it a chance to go away on it's own terms (like gibs)
        !          2163:                T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
        !          2164:                // if it's still there, nuke it
        !          2165:                if (other && other->inuse)
        !          2166:                        BecomeExplosion1 (other);
        !          2167:                return;
        !          2168:        }
        !          2169: 
        !          2170:        if (level.time < self->touch_debounce_time)
        !          2171:                return;
        !          2172: 
        !          2173:        if (!self->dmg)
        !          2174:                return;
        !          2175:        self->touch_debounce_time = level.time + 0.5;
        !          2176:        T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
        !          2177: }
        !          2178: 
        !          2179: void train_wait (edict_t *self)
        !          2180: {
        !          2181:        if (self->target_ent->pathtarget)
        !          2182:        {
        !          2183:                char    *savetarget;
        !          2184:                edict_t *ent;
        !          2185: 
        !          2186:                ent = self->target_ent;
        !          2187:                savetarget = ent->target;
        !          2188:                ent->target = ent->pathtarget;
        !          2189:                G_UseTargets (ent, self->activator);
        !          2190:                ent->target = savetarget;
        !          2191: 
        !          2192:                // make sure we didn't get killed by a killtarget
        !          2193:                if (!self->inuse)
        !          2194:                        return;
        !          2195:        }
        !          2196: 
        !          2197:        if (self->moveinfo.wait)
        !          2198:        {
        !          2199:                if (self->moveinfo.wait > 0)
        !          2200:                {
        !          2201:                        self->nextthink = level.time + self->moveinfo.wait;
        !          2202:                        self->think = train_next;
        !          2203:                }
        !          2204:                else if (self->spawnflags & TRAIN_TOGGLE)  // && wait < 0
        !          2205:                {
        !          2206:                        // PMM - clear target_ent, let train_next get called when we get used
        !          2207: //                     train_next (self);
        !          2208:                        self->target_ent = NULL;
        !          2209:                        // pmm
        !          2210:                        self->spawnflags &= ~TRAIN_START_ON;
        !          2211:                        VectorClear (self->velocity);
        !          2212:                        self->nextthink = 0;
        !          2213:                }
        !          2214: 
        !          2215:                if (!(self->flags & FL_TEAMSLAVE))
        !          2216:                {
        !          2217:                        if (self->moveinfo.sound_end)
        !          2218:                                gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_end, 1, ATTN_STATIC, 0);
        !          2219:                        self->s.sound = 0;
        !          2220:                }
        !          2221:        }
        !          2222:        else
        !          2223:        {
        !          2224:                train_next (self);
        !          2225:        }
        !          2226:        
        !          2227: }
        !          2228: 
        !          2229: //PGM
        !          2230: void train_piece_wait (edict_t *self)
        !          2231: {
        !          2232: }
        !          2233: //PGM
        !          2234: 
        !          2235: void train_next (edict_t *self)
        !          2236: {
        !          2237:        edict_t         *ent;
        !          2238:        vec3_t          dest;
        !          2239:        qboolean        first;
        !          2240: 
        !          2241:        first = true;
        !          2242: again:
        !          2243:        if (!self->target)
        !          2244:        {
        !          2245: //             gi.dprintf ("train_next: no next target\n");
        !          2246:                return;
        !          2247:        }
        !          2248: 
        !          2249:        ent = G_PickTarget (self->target);
        !          2250:        if (!ent)
        !          2251:        {
        !          2252:                gi.dprintf ("train_next: bad target %s\n", self->target);
        !          2253:                return;
        !          2254:        }
        !          2255: 
        !          2256:        self->target = ent->target;
        !          2257: 
        !          2258:        // check for a teleport path_corner
        !          2259:        if (ent->spawnflags & 1)
        !          2260:        {
        !          2261:                if (!first)
        !          2262:                {
        !          2263:                        gi.dprintf ("connected teleport path_corners, see %s at %s\n", ent->classname, vtos(ent->s.origin));
        !          2264:                        return;
        !          2265:                }
        !          2266:                first = false;
        !          2267:                VectorSubtract (ent->s.origin, self->mins, self->s.origin);
        !          2268:                VectorCopy (self->s.origin, self->s.old_origin);
        !          2269:                self->s.event = EV_OTHER_TELEPORT;
        !          2270:                gi.linkentity (self);
        !          2271:                goto again;
        !          2272:        }
        !          2273: 
        !          2274: //PGM
        !          2275:        if (ent->speed)
        !          2276:        {
        !          2277:                self->speed = ent->speed;
        !          2278:                self->moveinfo.speed = ent->speed;
        !          2279:                if(ent->accel)
        !          2280:                        self->moveinfo.accel = ent->accel;
        !          2281:                else
        !          2282:                        self->moveinfo.accel = ent->speed;
        !          2283:                if(ent->decel)
        !          2284:                        self->moveinfo.decel = ent->decel;
        !          2285:                else
        !          2286:                        self->moveinfo.decel = ent->speed;
        !          2287:                self->moveinfo.current_speed = 0;
        !          2288:        }
        !          2289: //PGM
        !          2290: 
        !          2291:        self->moveinfo.wait = ent->wait;
        !          2292:        self->target_ent = ent;
        !          2293: 
        !          2294:        if (!(self->flags & FL_TEAMSLAVE))
        !          2295:        {
        !          2296:                if (self->moveinfo.sound_start)
        !          2297:                        gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
        !          2298:                self->s.sound = self->moveinfo.sound_middle;
        !          2299:        }
        !          2300: 
        !          2301:        VectorSubtract (ent->s.origin, self->mins, dest);
        !          2302:        self->moveinfo.state = STATE_TOP;
        !          2303:        VectorCopy (self->s.origin, self->moveinfo.start_origin);
        !          2304:        VectorCopy (dest, self->moveinfo.end_origin);
        !          2305:        Move_Calc (self, dest, train_wait);
        !          2306:        self->spawnflags |= TRAIN_START_ON;
        !          2307:        
        !          2308: //PGM
        !          2309:        if(self->team)
        !          2310:        {
        !          2311:                edict_t *e;
        !          2312:                vec3_t  dir, dst;
        !          2313: 
        !          2314:                VectorSubtract (dest, self->s.origin, dir);
        !          2315:                for (e=self->teamchain; e ; e = e->teamchain)
        !          2316:                {
        !          2317:                        VectorAdd(dir, e->s.origin, dst);
        !          2318:                        VectorCopy(e->s.origin, e->moveinfo.start_origin);
        !          2319:                        VectorCopy(dst, e->moveinfo.end_origin);
        !          2320: 
        !          2321:                        e->moveinfo.state = STATE_TOP;
        !          2322:                        e->speed = self->speed;
        !          2323:                        e->moveinfo.speed = self->moveinfo.speed;
        !          2324:                        e->moveinfo.accel = self->moveinfo.accel;
        !          2325:                        e->moveinfo.decel = self->moveinfo.decel;
        !          2326:                        e->movetype = MOVETYPE_PUSH;
        !          2327:                        Move_Calc (e, dst, train_piece_wait);
        !          2328:                }
        !          2329:        
        !          2330:        }
        !          2331: //PGM
        !          2332: }
        !          2333: 
        !          2334: void train_resume (edict_t *self)
        !          2335: {
        !          2336:        edict_t *ent;
        !          2337:        vec3_t  dest;
        !          2338: 
        !          2339:        ent = self->target_ent;
        !          2340: 
        !          2341:        VectorSubtract (ent->s.origin, self->mins, dest);
        !          2342:        self->moveinfo.state = STATE_TOP;
        !          2343:        VectorCopy (self->s.origin, self->moveinfo.start_origin);
        !          2344:        VectorCopy (dest, self->moveinfo.end_origin);
        !          2345:        Move_Calc (self, dest, train_wait);
        !          2346:        self->spawnflags |= TRAIN_START_ON;
        !          2347: }
        !          2348: 
        !          2349: void func_train_find (edict_t *self)
        !          2350: {
        !          2351:        edict_t *ent;
        !          2352: 
        !          2353:        if (!self->target)
        !          2354:        {
        !          2355:                gi.dprintf ("train_find: no target\n");
        !          2356:                return;
        !          2357:        }
        !          2358:        ent = G_PickTarget (self->target);
        !          2359:        if (!ent)
        !          2360:        {
        !          2361:                gi.dprintf ("train_find: target %s not found\n", self->target);
        !          2362:                return;
        !          2363:        }
        !          2364:        self->target = ent->target;
        !          2365: 
        !          2366:        VectorSubtract (ent->s.origin, self->mins, self->s.origin);
        !          2367:        gi.linkentity (self);
        !          2368: 
        !          2369:        // if not triggered, start immediately
        !          2370:        if (!self->targetname)
        !          2371:                self->spawnflags |= TRAIN_START_ON;
        !          2372: 
        !          2373:        if (self->spawnflags & TRAIN_START_ON)
        !          2374:        {
        !          2375:                self->nextthink = level.time + FRAMETIME;
        !          2376:                self->think = train_next;
        !          2377:                self->activator = self;
        !          2378:        }
        !          2379: }
        !          2380: 
        !          2381: void train_use (edict_t *self, edict_t *other, edict_t *activator)
        !          2382: {
        !          2383:        self->activator = activator;
        !          2384: 
        !          2385:        if (self->spawnflags & TRAIN_START_ON)
        !          2386:        {
        !          2387:                if (!(self->spawnflags & TRAIN_TOGGLE))
        !          2388:                        return;
        !          2389:                self->spawnflags &= ~TRAIN_START_ON;
        !          2390:                VectorClear (self->velocity);
        !          2391:                self->nextthink = 0;
        !          2392:        }
        !          2393:        else
        !          2394:        {
        !          2395:                if (self->target_ent)
        !          2396:                        train_resume(self);
        !          2397:                else
        !          2398:                        train_next(self);
        !          2399:        }
        !          2400: }
        !          2401: 
        !          2402: void SP_func_train (edict_t *self)
        !          2403: {
        !          2404:        self->movetype = MOVETYPE_PUSH;
        !          2405: 
        !          2406:        VectorClear (self->s.angles);
        !          2407:        self->blocked = train_blocked;
        !          2408:        if (self->spawnflags & TRAIN_BLOCK_STOPS)
        !          2409:                self->dmg = 0;
        !          2410:        else
        !          2411:        {
        !          2412:                if (!self->dmg)
        !          2413:                        self->dmg = 100;
        !          2414:        }
        !          2415:        self->solid = SOLID_BSP;
        !          2416:        gi.setmodel (self, self->model);
        !          2417: 
        !          2418:        if (st.noise)
        !          2419:                self->moveinfo.sound_middle = gi.soundindex  (st.noise);
        !          2420: 
        !          2421:        if (!self->speed)
        !          2422:                self->speed = 100;
        !          2423: 
        !          2424:        self->moveinfo.speed = self->speed;
        !          2425:        self->moveinfo.accel = self->moveinfo.decel = self->moveinfo.speed;
        !          2426: 
        !          2427:        self->use = train_use;
        !          2428: 
        !          2429:        gi.linkentity (self);
        !          2430: 
        !          2431:        if (self->target)
        !          2432:        {
        !          2433:                // start trains on the second frame, to make sure their targets have had
        !          2434:                // a chance to spawn
        !          2435:                self->nextthink = level.time + FRAMETIME;
        !          2436:                self->think = func_train_find;
        !          2437:        }
        !          2438:        else
        !          2439:        {
        !          2440:                gi.dprintf ("func_train without a target at %s\n", vtos(self->absmin));
        !          2441:        }
        !          2442: }
        !          2443: 
        !          2444: 
        !          2445: /*QUAKED trigger_elevator (0.3 0.1 0.6) (-8 -8 -8) (8 8 8)
        !          2446: */
        !          2447: void trigger_elevator_use (edict_t *self, edict_t *other, edict_t *activator)
        !          2448: {
        !          2449:        edict_t *target;
        !          2450: 
        !          2451:        if (self->movetarget->nextthink)
        !          2452:        {
        !          2453: //             gi.dprintf("elevator busy\n");
        !          2454:                return;
        !          2455:        }
        !          2456: 
        !          2457:        if (!other->pathtarget)
        !          2458:        {
        !          2459:                gi.dprintf("elevator used with no pathtarget\n");
        !          2460:                return;
        !          2461:        }
        !          2462: 
        !          2463:        target = G_PickTarget (other->pathtarget);
        !          2464:        if (!target)
        !          2465:        {
        !          2466:                gi.dprintf("elevator used with bad pathtarget: %s\n", other->pathtarget);
        !          2467:                return;
        !          2468:        }
        !          2469: 
        !          2470:        self->movetarget->target_ent = target;
        !          2471:        train_resume (self->movetarget);
        !          2472: }
        !          2473: 
        !          2474: void trigger_elevator_init (edict_t *self)
        !          2475: {
        !          2476:        if (!self->target)
        !          2477:        {
        !          2478:                gi.dprintf("trigger_elevator has no target\n");
        !          2479:                return;
        !          2480:        }
        !          2481:        self->movetarget = G_PickTarget (self->target);
        !          2482:        if (!self->movetarget)
        !          2483:        {
        !          2484:                gi.dprintf("trigger_elevator unable to find target %s\n", self->target);
        !          2485:                return;
        !          2486:        }
        !          2487:        if (strcmp(self->movetarget->classname, "func_train") != 0)
        !          2488:        {
        !          2489:                gi.dprintf("trigger_elevator target %s is not a train\n", self->target);
        !          2490:                return;
        !          2491:        }
        !          2492: 
        !          2493:        self->use = trigger_elevator_use;
        !          2494:        self->svflags = SVF_NOCLIENT;
        !          2495: 
        !          2496: }
        !          2497: 
        !          2498: void SP_trigger_elevator (edict_t *self)
        !          2499: {
        !          2500:        self->think = trigger_elevator_init;
        !          2501:        self->nextthink = level.time + FRAMETIME;
        !          2502: }
        !          2503: 
        !          2504: 
        !          2505: /*QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON
        !          2506: "wait"                 base time between triggering all targets, default is 1
        !          2507: "random"               wait variance, default is 0
        !          2508: 
        !          2509: so, the basic time between firing is a random time between
        !          2510: (wait - random) and (wait + random)
        !          2511: 
        !          2512: "delay"                        delay before first firing when turned on, default is 0
        !          2513: 
        !          2514: "pausetime"            additional delay used only the very first time
        !          2515:                                and only if spawned with START_ON
        !          2516: 
        !          2517: These can used but not touched.
        !          2518: */
        !          2519: void func_timer_think (edict_t *self)
        !          2520: {
        !          2521:        G_UseTargets (self, self->activator);
        !          2522:        self->nextthink = level.time + self->wait + crandom() * self->random;
        !          2523: }
        !          2524: 
        !          2525: void func_timer_use (edict_t *self, edict_t *other, edict_t *activator)
        !          2526: {
        !          2527:        self->activator = activator;
        !          2528: 
        !          2529:        // if on, turn it off
        !          2530:        if (self->nextthink)
        !          2531:        {
        !          2532:                self->nextthink = 0;
        !          2533:                return;
        !          2534:        }
        !          2535: 
        !          2536:        // turn it on
        !          2537:        if (self->delay)
        !          2538:                self->nextthink = level.time + self->delay;
        !          2539:        else
        !          2540:                func_timer_think (self);
        !          2541: }
        !          2542: 
        !          2543: void SP_func_timer (edict_t *self)
        !          2544: {
        !          2545:        if (!self->wait)
        !          2546:                self->wait = 1.0;
        !          2547: 
        !          2548:        self->use = func_timer_use;
        !          2549:        self->think = func_timer_think;
        !          2550: 
        !          2551:        if (self->random >= self->wait)
        !          2552:        {
        !          2553:                self->random = self->wait - FRAMETIME;
        !          2554:                gi.dprintf("func_timer at %s has random >= wait\n", vtos(self->s.origin));
        !          2555:        }
        !          2556: 
        !          2557:        if (self->spawnflags & 1)
        !          2558:        {
        !          2559:                self->nextthink = level.time + 1.0 + st.pausetime + self->delay + self->wait + crandom() * self->random;
        !          2560:                self->activator = self;
        !          2561:        }
        !          2562: 
        !          2563:        self->svflags = SVF_NOCLIENT;
        !          2564: }
        !          2565: 
        !          2566: 
        !          2567: /*QUAKED func_conveyor (0 .5 .8) ? START_ON TOGGLE
        !          2568: Conveyors are stationary brushes that move what's on them.
        !          2569: The brush should be have a surface with at least one current content enabled.
        !          2570: speed  default 100
        !          2571: */
        !          2572: 
        !          2573: void func_conveyor_use (edict_t *self, edict_t *other, edict_t *activator)
        !          2574: {
        !          2575:        if (self->spawnflags & 1)
        !          2576:        {
        !          2577:                self->speed = 0;
        !          2578:                self->spawnflags &= ~1;
        !          2579:        }
        !          2580:        else
        !          2581:        {
        !          2582:                self->speed = self->count;
        !          2583:                self->spawnflags |= 1;
        !          2584:        }
        !          2585: 
        !          2586:        if (!(self->spawnflags & 2))
        !          2587:                self->count = 0;
        !          2588: }
        !          2589: 
        !          2590: void SP_func_conveyor (edict_t *self)
        !          2591: {
        !          2592:        if (!self->speed)
        !          2593:                self->speed = 100;
        !          2594: 
        !          2595:        if (!(self->spawnflags & 1))
        !          2596:        {
        !          2597:                self->count = self->speed;
        !          2598:                self->speed = 0;
        !          2599:        }
        !          2600: 
        !          2601:        self->use = func_conveyor_use;
        !          2602: 
        !          2603:        gi.setmodel (self, self->model);
        !          2604:        self->solid = SOLID_BSP;
        !          2605:        gi.linkentity (self);
        !          2606: }
        !          2607: 
        !          2608: 
        !          2609: /*QUAKED func_door_secret (0 .5 .8) ? always_shoot 1st_left 1st_down
        !          2610: A secret door.  Slide back and then to the side.
        !          2611: 
        !          2612: open_once              doors never closes
        !          2613: 1st_left               1st move is left of arrow
        !          2614: 1st_down               1st move is down from arrow
        !          2615: always_shoot   door is shootebale even if targeted
        !          2616: 
        !          2617: "angle"                determines the direction
        !          2618: "dmg"          damage to inflic when blocked (default 2)
        !          2619: "wait"         how long to hold in the open position (default 5, -1 means hold)
        !          2620: */
        !          2621: 
        !          2622: #define SECRET_ALWAYS_SHOOT    1
        !          2623: #define SECRET_1ST_LEFT                2
        !          2624: #define SECRET_1ST_DOWN                4
        !          2625: 
        !          2626: void door_secret_move1 (edict_t *self);
        !          2627: void door_secret_move2 (edict_t *self);
        !          2628: void door_secret_move3 (edict_t *self);
        !          2629: void door_secret_move4 (edict_t *self);
        !          2630: void door_secret_move5 (edict_t *self);
        !          2631: void door_secret_move6 (edict_t *self);
        !          2632: void door_secret_done (edict_t *self);
        !          2633: 
        !          2634: void door_secret_use (edict_t *self, edict_t *other, edict_t *activator)
        !          2635: {
        !          2636:        // make sure we're not already moving
        !          2637:        if (!VectorCompare(self->s.origin, vec3_origin))
        !          2638:                return;
        !          2639: 
        !          2640:        Move_Calc (self, self->pos1, door_secret_move1);
        !          2641:        door_use_areaportals (self, true);
        !          2642: }
        !          2643: 
        !          2644: void door_secret_move1 (edict_t *self)
        !          2645: {
        !          2646:        self->nextthink = level.time + 1.0;
        !          2647:        self->think = door_secret_move2;
        !          2648: }
        !          2649: 
        !          2650: void door_secret_move2 (edict_t *self)
        !          2651: {
        !          2652:        Move_Calc (self, self->pos2, door_secret_move3);
        !          2653: }
        !          2654: 
        !          2655: void door_secret_move3 (edict_t *self)
        !          2656: {
        !          2657:        if (self->wait == -1)
        !          2658:                return;
        !          2659:        self->nextthink = level.time + self->wait;
        !          2660:        self->think = door_secret_move4;
        !          2661: }
        !          2662: 
        !          2663: void door_secret_move4 (edict_t *self)
        !          2664: {
        !          2665:        Move_Calc (self, self->pos1, door_secret_move5);
        !          2666: }
        !          2667: 
        !          2668: void door_secret_move5 (edict_t *self)
        !          2669: {
        !          2670:        self->nextthink = level.time + 1.0;
        !          2671:        self->think = door_secret_move6;
        !          2672: }
        !          2673: 
        !          2674: void door_secret_move6 (edict_t *self)
        !          2675: {
        !          2676:        Move_Calc (self, vec3_origin, door_secret_done);
        !          2677: }
        !          2678: 
        !          2679: void door_secret_done (edict_t *self)
        !          2680: {
        !          2681:        if (!(self->targetname) || (self->spawnflags & SECRET_ALWAYS_SHOOT))
        !          2682:        {
        !          2683:                self->health = 0;
        !          2684:                self->takedamage = DAMAGE_YES;
        !          2685:        }
        !          2686:        door_use_areaportals (self, false);
        !          2687: }
        !          2688: 
        !          2689: void door_secret_blocked  (edict_t *self, edict_t *other)
        !          2690: {
        !          2691:        if (!(other->svflags & SVF_MONSTER) && (!other->client) )
        !          2692:        {
        !          2693:                // give it a chance to go away on it's own terms (like gibs)
        !          2694:                T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
        !          2695:                // if it's still there, nuke it
        !          2696:                if (other && other->inuse)
        !          2697:                        BecomeExplosion1 (other);
        !          2698:                return;
        !          2699:        }
        !          2700: 
        !          2701:        if (level.time < self->touch_debounce_time)
        !          2702:                return;
        !          2703:        self->touch_debounce_time = level.time + 0.5;
        !          2704: 
        !          2705:        T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
        !          2706: }
        !          2707: 
        !          2708: void door_secret_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
        !          2709: {
        !          2710:        self->takedamage = DAMAGE_NO;
        !          2711:        door_secret_use (self, attacker, attacker);
        !          2712: }
        !          2713: 
        !          2714: void SP_func_door_secret (edict_t *ent)
        !          2715: {
        !          2716:        vec3_t  forward, right, up;
        !          2717:        float   side;
        !          2718:        float   width;
        !          2719:        float   length;
        !          2720: 
        !          2721:        ent->moveinfo.sound_start = gi.soundindex  ("doors/dr1_strt.wav");
        !          2722:        ent->moveinfo.sound_middle = gi.soundindex  ("doors/dr1_mid.wav");
        !          2723:        ent->moveinfo.sound_end = gi.soundindex  ("doors/dr1_end.wav");
        !          2724: 
        !          2725:        ent->movetype = MOVETYPE_PUSH;
        !          2726:        ent->solid = SOLID_BSP;
        !          2727:        gi.setmodel (ent, ent->model);
        !          2728: 
        !          2729:        ent->blocked = door_secret_blocked;
        !          2730:        ent->use = door_secret_use;
        !          2731: 
        !          2732:        if (!(ent->targetname) || (ent->spawnflags & SECRET_ALWAYS_SHOOT))
        !          2733:        {
        !          2734:                ent->health = 0;
        !          2735:                ent->takedamage = DAMAGE_YES;
        !          2736:                ent->die = door_secret_die;
        !          2737:        }
        !          2738: 
        !          2739:        if (!ent->dmg)
        !          2740:                ent->dmg = 2;
        !          2741: 
        !          2742:        if (!ent->wait)
        !          2743:                ent->wait = 5;
        !          2744: 
        !          2745:        ent->moveinfo.accel =
        !          2746:        ent->moveinfo.decel =
        !          2747:        ent->moveinfo.speed = 50;
        !          2748: 
        !          2749:        // calculate positions
        !          2750:        AngleVectors (ent->s.angles, forward, right, up);
        !          2751:        VectorClear (ent->s.angles);
        !          2752:        side = 1.0 - (ent->spawnflags & SECRET_1ST_LEFT);
        !          2753:        if (ent->spawnflags & SECRET_1ST_DOWN)
        !          2754:                width = fabs(DotProduct(up, ent->size));
        !          2755:        else
        !          2756:                width = fabs(DotProduct(right, ent->size));
        !          2757:        length = fabs(DotProduct(forward, ent->size));
        !          2758:        if (ent->spawnflags & SECRET_1ST_DOWN)
        !          2759:                VectorMA (ent->s.origin, -1 * width, up, ent->pos1);
        !          2760:        else
        !          2761:                VectorMA (ent->s.origin, side * width, right, ent->pos1);
        !          2762:        VectorMA (ent->pos1, length, forward, ent->pos2);
        !          2763: 
        !          2764:        if (ent->health)
        !          2765:        {
        !          2766:                ent->takedamage = DAMAGE_YES;
        !          2767:                ent->die = door_killed;
        !          2768:                ent->max_health = ent->health;
        !          2769:        }
        !          2770:        else if (ent->targetname && ent->message)
        !          2771:        {
        !          2772:                gi.soundindex ("misc/talk.wav");
        !          2773:                ent->touch = door_touch;
        !          2774:        }
        !          2775:        
        !          2776:        ent->classname = "func_door";
        !          2777: 
        !          2778:        gi.linkentity (ent);
        !          2779: }
        !          2780: 
        !          2781: 
        !          2782: /*QUAKED func_killbox (1 0 0) ?
        !          2783: Kills everything inside when fired, irrespective of protection.
        !          2784: */
        !          2785: void use_killbox (edict_t *self, edict_t *other, edict_t *activator)
        !          2786: {
        !          2787:        KillBox (self);
        !          2788: }
        !          2789: 
        !          2790: void SP_func_killbox (edict_t *ent)
        !          2791: {
        !          2792:        gi.setmodel (ent, ent->model);
        !          2793:        ent->use = use_killbox;
        !          2794:        ent->svflags = SVF_NOCLIENT;
        !          2795: }
        !          2796: 

unix.superglobalmegacorp.com

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