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

unix.superglobalmegacorp.com

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