Annotation of quake2/xatrix/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:                self->s.event = EV_OTHER_TELEPORT;
        !          1545:                gi.linkentity (self);
        !          1546:                goto again;
        !          1547:        }
        !          1548: 
        !          1549:        self->moveinfo.wait = ent->wait;
        !          1550:        self->target_ent = ent;
        !          1551: 
        !          1552:        if (!(self->flags & FL_TEAMSLAVE))
        !          1553:        {
        !          1554:                if (self->moveinfo.sound_start)
        !          1555:                        gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
        !          1556:                self->s.sound = self->moveinfo.sound_middle;
        !          1557:        }
        !          1558: 
        !          1559:        VectorSubtract (ent->s.origin, self->mins, dest);
        !          1560:        self->moveinfo.state = STATE_TOP;
        !          1561:        VectorCopy (self->s.origin, self->moveinfo.start_origin);
        !          1562:        VectorCopy (dest, self->moveinfo.end_origin);
        !          1563:        Move_Calc (self, dest, train_wait);
        !          1564:        self->spawnflags |= TRAIN_START_ON;
        !          1565: }
        !          1566: 
        !          1567: void train_resume (edict_t *self)
        !          1568: {
        !          1569:        edict_t *ent;
        !          1570:        vec3_t  dest;
        !          1571: 
        !          1572:        ent = self->target_ent;
        !          1573: 
        !          1574:        VectorSubtract (ent->s.origin, self->mins, dest);
        !          1575:        self->moveinfo.state = STATE_TOP;
        !          1576:        VectorCopy (self->s.origin, self->moveinfo.start_origin);
        !          1577:        VectorCopy (dest, self->moveinfo.end_origin);
        !          1578:        Move_Calc (self, dest, train_wait);
        !          1579:        self->spawnflags |= TRAIN_START_ON;
        !          1580: }
        !          1581: 
        !          1582: void func_train_find (edict_t *self)
        !          1583: {
        !          1584:        edict_t *ent;
        !          1585: 
        !          1586:        if (!self->target)
        !          1587:        {
        !          1588:                gi.dprintf ("train_find: no target\n");
        !          1589:                return;
        !          1590:        }
        !          1591:        ent = G_PickTarget (self->target);
        !          1592:        if (!ent)
        !          1593:        {
        !          1594:                gi.dprintf ("train_find: target %s not found\n", self->target);
        !          1595:                return;
        !          1596:        }
        !          1597:        self->target = ent->target;
        !          1598: 
        !          1599:        VectorSubtract (ent->s.origin, self->mins, self->s.origin);
        !          1600:        gi.linkentity (self);
        !          1601: 
        !          1602:        // if not triggered, start immediately
        !          1603:        if (!self->targetname)
        !          1604:                self->spawnflags |= TRAIN_START_ON;
        !          1605: 
        !          1606:        if (self->spawnflags & TRAIN_START_ON)
        !          1607:        {
        !          1608:                self->nextthink = level.time + FRAMETIME;
        !          1609:                self->think = train_next;
        !          1610:                self->activator = self;
        !          1611:        }
        !          1612: }
        !          1613: 
        !          1614: void train_use (edict_t *self, edict_t *other, edict_t *activator)
        !          1615: {
        !          1616:        self->activator = activator;
        !          1617: 
        !          1618:        if (self->spawnflags & TRAIN_START_ON)
        !          1619:        {
        !          1620:                if (!(self->spawnflags & TRAIN_TOGGLE))
        !          1621:                        return;
        !          1622:                self->spawnflags &= ~TRAIN_START_ON;
        !          1623:                VectorClear (self->velocity);
        !          1624:                self->nextthink = 0;
        !          1625:        }
        !          1626:        else
        !          1627:        {
        !          1628:                if (self->target_ent)
        !          1629:                        train_resume(self);
        !          1630:                else
        !          1631:                        train_next(self);
        !          1632:        }
        !          1633: }
        !          1634: 
        !          1635: void SP_func_train (edict_t *self)
        !          1636: {
        !          1637:        self->movetype = MOVETYPE_PUSH;
        !          1638: 
        !          1639:        VectorClear (self->s.angles);
        !          1640:        self->blocked = train_blocked;
        !          1641:        if (self->spawnflags & TRAIN_BLOCK_STOPS)
        !          1642:                self->dmg = 0;
        !          1643:        else
        !          1644:        {
        !          1645:                if (!self->dmg)
        !          1646:                        self->dmg = 100;
        !          1647:        }
        !          1648:        self->solid = SOLID_BSP;
        !          1649:        gi.setmodel (self, self->model);
        !          1650: 
        !          1651:        if (st.noise)
        !          1652:                self->moveinfo.sound_middle = gi.soundindex  (st.noise);
        !          1653: 
        !          1654:        if (!self->speed)
        !          1655:                self->speed = 100;
        !          1656: 
        !          1657:        self->moveinfo.speed = self->speed;
        !          1658:        self->moveinfo.accel = self->moveinfo.decel = self->moveinfo.speed;
        !          1659: 
        !          1660:        self->use = train_use;
        !          1661: 
        !          1662:        gi.linkentity (self);
        !          1663: 
        !          1664:        if (self->target)
        !          1665:        {
        !          1666:                // start trains on the second frame, to make sure their targets have had
        !          1667:                // a chance to spawn
        !          1668:                self->nextthink = level.time + FRAMETIME;
        !          1669:                self->think = func_train_find;
        !          1670:        }
        !          1671:        else
        !          1672:        {
        !          1673:                gi.dprintf ("func_train without a target at %s\n", vtos(self->absmin));
        !          1674:        }
        !          1675: }
        !          1676: 
        !          1677: 
        !          1678: /*QUAKED trigger_elevator (0.3 0.1 0.6) (-8 -8 -8) (8 8 8)
        !          1679: */
        !          1680: void trigger_elevator_use (edict_t *self, edict_t *other, edict_t *activator)
        !          1681: {
        !          1682:        edict_t *target;
        !          1683: 
        !          1684:        if (self->movetarget->nextthink)
        !          1685:        {
        !          1686: //             gi.dprintf("elevator busy\n");
        !          1687:                return;
        !          1688:        }
        !          1689: 
        !          1690:        if (!other->pathtarget)
        !          1691:        {
        !          1692:                gi.dprintf("elevator used with no pathtarget\n");
        !          1693:                return;
        !          1694:        }
        !          1695: 
        !          1696:        target = G_PickTarget (other->pathtarget);
        !          1697:        if (!target)
        !          1698:        {
        !          1699:                gi.dprintf("elevator used with bad pathtarget: %s\n", other->pathtarget);
        !          1700:                return;
        !          1701:        }
        !          1702: 
        !          1703:        self->movetarget->target_ent = target;
        !          1704:        train_resume (self->movetarget);
        !          1705: }
        !          1706: 
        !          1707: void trigger_elevator_init (edict_t *self)
        !          1708: {
        !          1709:        if (!self->target)
        !          1710:        {
        !          1711:                gi.dprintf("trigger_elevator has no target\n");
        !          1712:                return;
        !          1713:        }
        !          1714:        self->movetarget = G_PickTarget (self->target);
        !          1715:        if (!self->movetarget)
        !          1716:        {
        !          1717:                gi.dprintf("trigger_elevator unable to find target %s\n", self->target);
        !          1718:                return;
        !          1719:        }
        !          1720:        if (strcmp(self->movetarget->classname, "func_train") != 0)
        !          1721:        {
        !          1722:                gi.dprintf("trigger_elevator target %s is not a train\n", self->target);
        !          1723:                return;
        !          1724:        }
        !          1725: 
        !          1726:        self->use = trigger_elevator_use;
        !          1727:        self->svflags = SVF_NOCLIENT;
        !          1728: 
        !          1729: }
        !          1730: 
        !          1731: void SP_trigger_elevator (edict_t *self)
        !          1732: {
        !          1733:        self->think = trigger_elevator_init;
        !          1734:        self->nextthink = level.time + FRAMETIME;
        !          1735: }
        !          1736: 
        !          1737: 
        !          1738: /*QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON
        !          1739: "wait"                 base time between triggering all targets, default is 1
        !          1740: "random"               wait variance, default is 0
        !          1741: 
        !          1742: so, the basic time between firing is a random time between
        !          1743: (wait - random) and (wait + random)
        !          1744: 
        !          1745: "delay"                        delay before first firing when turned on, default is 0
        !          1746: 
        !          1747: "pausetime"            additional delay used only the very first time
        !          1748:                                and only if spawned with START_ON
        !          1749: 
        !          1750: These can used but not touched.
        !          1751: */
        !          1752: void func_timer_think (edict_t *self)
        !          1753: {
        !          1754:        G_UseTargets (self, self->activator);
        !          1755:        self->nextthink = level.time + self->wait + crandom() * self->random;
        !          1756: }
        !          1757: 
        !          1758: void func_timer_use (edict_t *self, edict_t *other, edict_t *activator)
        !          1759: {
        !          1760:        self->activator = activator;
        !          1761: 
        !          1762:        // if on, turn it off
        !          1763:        if (self->nextthink)
        !          1764:        {
        !          1765:                self->nextthink = 0;
        !          1766:                return;
        !          1767:        }
        !          1768: 
        !          1769:        // turn it on
        !          1770:        if (self->delay)
        !          1771:                self->nextthink = level.time + self->delay;
        !          1772:        else
        !          1773:                func_timer_think (self);
        !          1774: }
        !          1775: 
        !          1776: void SP_func_timer (edict_t *self)
        !          1777: {
        !          1778:        if (!self->wait)
        !          1779:                self->wait = 1.0;
        !          1780: 
        !          1781:        self->use = func_timer_use;
        !          1782:        self->think = func_timer_think;
        !          1783: 
        !          1784:        if (self->random >= self->wait)
        !          1785:        {
        !          1786:                self->random = self->wait - FRAMETIME;
        !          1787:                gi.dprintf("func_timer at %s has random >= wait\n", vtos(self->s.origin));
        !          1788:        }
        !          1789: 
        !          1790:        if (self->spawnflags & 1)
        !          1791:        {
        !          1792:                self->nextthink = level.time + 1.0 + st.pausetime + self->delay + self->wait + crandom() * self->random;
        !          1793:                self->activator = self;
        !          1794:        }
        !          1795: 
        !          1796:        self->svflags = SVF_NOCLIENT;
        !          1797: }
        !          1798: 
        !          1799: 
        !          1800: /*QUAKED func_conveyor (0 .5 .8) ? START_ON TOGGLE
        !          1801: Conveyors are stationary brushes that move what's on them.
        !          1802: The brush should be have a surface with at least one current content enabled.
        !          1803: speed  default 100
        !          1804: */
        !          1805: 
        !          1806: void func_conveyor_use (edict_t *self, edict_t *other, edict_t *activator)
        !          1807: {
        !          1808:        if (self->spawnflags & 1)
        !          1809:        {
        !          1810:                self->speed = 0;
        !          1811:                self->spawnflags &= ~1;
        !          1812:        }
        !          1813:        else
        !          1814:        {
        !          1815:                self->speed = self->count;
        !          1816:                self->spawnflags |= 1;
        !          1817:        }
        !          1818: 
        !          1819:        if (!(self->spawnflags & 2))
        !          1820:                self->count = 0;
        !          1821: }
        !          1822: 
        !          1823: void SP_func_conveyor (edict_t *self)
        !          1824: {
        !          1825:        if (!self->speed)
        !          1826:                self->speed = 100;
        !          1827: 
        !          1828:        if (!(self->spawnflags & 1))
        !          1829:        {
        !          1830:                self->count = self->speed;
        !          1831:                self->speed = 0;
        !          1832:        }
        !          1833: 
        !          1834:        self->use = func_conveyor_use;
        !          1835: 
        !          1836:        gi.setmodel (self, self->model);
        !          1837:        self->solid = SOLID_BSP;
        !          1838:        gi.linkentity (self);
        !          1839: }
        !          1840: 
        !          1841: 
        !          1842: /*QUAKED func_door_secret (0 .5 .8) ? always_shoot 1st_left 1st_down
        !          1843: A secret door.  Slide back and then to the side.
        !          1844: 
        !          1845: open_once              doors never closes
        !          1846: 1st_left               1st move is left of arrow
        !          1847: 1st_down               1st move is down from arrow
        !          1848: always_shoot   door is shootebale even if targeted
        !          1849: 
        !          1850: "angle"                determines the direction
        !          1851: "dmg"          damage to inflic when blocked (default 2)
        !          1852: "wait"         how long to hold in the open position (default 5, -1 means hold)
        !          1853: */
        !          1854: 
        !          1855: #define SECRET_ALWAYS_SHOOT    1
        !          1856: #define SECRET_1ST_LEFT                2
        !          1857: #define SECRET_1ST_DOWN                4
        !          1858: 
        !          1859: void door_secret_move1 (edict_t *self);
        !          1860: void door_secret_move2 (edict_t *self);
        !          1861: void door_secret_move3 (edict_t *self);
        !          1862: void door_secret_move4 (edict_t *self);
        !          1863: void door_secret_move5 (edict_t *self);
        !          1864: void door_secret_move6 (edict_t *self);
        !          1865: void door_secret_done (edict_t *self);
        !          1866: 
        !          1867: void door_secret_use (edict_t *self, edict_t *other, edict_t *activator)
        !          1868: {
        !          1869:        // make sure we're not already moving
        !          1870:        if (!VectorCompare(self->s.origin, vec3_origin))
        !          1871:                return;
        !          1872: 
        !          1873:        Move_Calc (self, self->pos1, door_secret_move1);
        !          1874:        door_use_areaportals (self, true);
        !          1875: }
        !          1876: 
        !          1877: void door_secret_move1 (edict_t *self)
        !          1878: {
        !          1879:        self->nextthink = level.time + 1.0;
        !          1880:        self->think = door_secret_move2;
        !          1881: }
        !          1882: 
        !          1883: void door_secret_move2 (edict_t *self)
        !          1884: {
        !          1885:        Move_Calc (self, self->pos2, door_secret_move3);
        !          1886: }
        !          1887: 
        !          1888: void door_secret_move3 (edict_t *self)
        !          1889: {
        !          1890:        if (self->wait == -1)
        !          1891:                return;
        !          1892:        self->nextthink = level.time + self->wait;
        !          1893:        self->think = door_secret_move4;
        !          1894: }
        !          1895: 
        !          1896: void door_secret_move4 (edict_t *self)
        !          1897: {
        !          1898:        Move_Calc (self, self->pos1, door_secret_move5);
        !          1899: }
        !          1900: 
        !          1901: void door_secret_move5 (edict_t *self)
        !          1902: {
        !          1903:        self->nextthink = level.time + 1.0;
        !          1904:        self->think = door_secret_move6;
        !          1905: }
        !          1906: 
        !          1907: void door_secret_move6 (edict_t *self)
        !          1908: {
        !          1909:        Move_Calc (self, vec3_origin, door_secret_done);
        !          1910: }
        !          1911: 
        !          1912: void door_secret_done (edict_t *self)
        !          1913: {
        !          1914:        if (!(self->targetname) || (self->spawnflags & SECRET_ALWAYS_SHOOT))
        !          1915:        {
        !          1916:                self->health = 0;
        !          1917:                self->takedamage = DAMAGE_YES;
        !          1918:        }
        !          1919:        door_use_areaportals (self, false);
        !          1920: }
        !          1921: 
        !          1922: void door_secret_blocked  (edict_t *self, edict_t *other)
        !          1923: {
        !          1924:        if (!(other->svflags & SVF_MONSTER) && (!other->client) )
        !          1925:        {
        !          1926:                // give it a chance to go away on it's own terms (like gibs)
        !          1927:                T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
        !          1928:                // if it's still there, nuke it
        !          1929:                if (other)
        !          1930:                        BecomeExplosion1 (other);
        !          1931:                return;
        !          1932:        }
        !          1933: 
        !          1934:        if (level.time < self->touch_debounce_time)
        !          1935:                return;
        !          1936:        self->touch_debounce_time = level.time + 0.5;
        !          1937: 
        !          1938:        T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
        !          1939: }
        !          1940: 
        !          1941: void door_secret_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
        !          1942: {
        !          1943:        self->takedamage = DAMAGE_NO;
        !          1944:        door_secret_use (self, attacker, attacker);
        !          1945: }
        !          1946: 
        !          1947: void SP_func_door_secret (edict_t *ent)
        !          1948: {
        !          1949:        vec3_t  forward, right, up;
        !          1950:        float   side;
        !          1951:        float   width;
        !          1952:        float   length;
        !          1953: 
        !          1954:        ent->moveinfo.sound_start = gi.soundindex  ("doors/dr1_strt.wav");
        !          1955:        ent->moveinfo.sound_middle = gi.soundindex  ("doors/dr1_mid.wav");
        !          1956:        ent->moveinfo.sound_end = gi.soundindex  ("doors/dr1_end.wav");
        !          1957: 
        !          1958:        ent->movetype = MOVETYPE_PUSH;
        !          1959:        ent->solid = SOLID_BSP;
        !          1960:        gi.setmodel (ent, ent->model);
        !          1961: 
        !          1962:        ent->blocked = door_secret_blocked;
        !          1963:        ent->use = door_secret_use;
        !          1964: 
        !          1965:        if (!(ent->targetname) || (ent->spawnflags & SECRET_ALWAYS_SHOOT))
        !          1966:        {
        !          1967:                ent->health = 0;
        !          1968:                ent->takedamage = DAMAGE_YES;
        !          1969:                ent->die = door_secret_die;
        !          1970:        }
        !          1971: 
        !          1972:        if (!ent->dmg)
        !          1973:                ent->dmg = 2;
        !          1974: 
        !          1975:        if (!ent->wait)
        !          1976:                ent->wait = 5;
        !          1977: 
        !          1978:        ent->moveinfo.accel =
        !          1979:        ent->moveinfo.decel =
        !          1980:        ent->moveinfo.speed = 50;
        !          1981: 
        !          1982:        // calculate positions
        !          1983:        AngleVectors (ent->s.angles, forward, right, up);
        !          1984:        VectorClear (ent->s.angles);
        !          1985:        side = 1.0 - (ent->spawnflags & SECRET_1ST_LEFT);
        !          1986:        if (ent->spawnflags & SECRET_1ST_DOWN)
        !          1987:                width = fabs(DotProduct(up, ent->size));
        !          1988:        else
        !          1989:                width = fabs(DotProduct(right, ent->size));
        !          1990:        length = fabs(DotProduct(forward, ent->size));
        !          1991:        if (ent->spawnflags & SECRET_1ST_DOWN)
        !          1992:                VectorMA (ent->s.origin, -1 * width, up, ent->pos1);
        !          1993:        else
        !          1994:                VectorMA (ent->s.origin, side * width, right, ent->pos1);
        !          1995:        VectorMA (ent->pos1, length, forward, ent->pos2);
        !          1996: 
        !          1997:        if (ent->health)
        !          1998:        {
        !          1999:                ent->takedamage = DAMAGE_YES;
        !          2000:                ent->die = door_killed;
        !          2001:                ent->max_health = ent->health;
        !          2002:        }
        !          2003:        else if (ent->targetname && ent->message)
        !          2004:        {
        !          2005:                gi.soundindex ("misc/talk.wav");
        !          2006:                ent->touch = door_touch;
        !          2007:        }
        !          2008:        
        !          2009:        ent->classname = "func_door";
        !          2010: 
        !          2011:        gi.linkentity (ent);
        !          2012: }
        !          2013: 
        !          2014: 
        !          2015: /*QUAKED func_killbox (1 0 0) ?
        !          2016: Kills everything inside when fired, irrespective of protection.
        !          2017: */
        !          2018: void use_killbox (edict_t *self, edict_t *other, edict_t *activator)
        !          2019: {
        !          2020:        KillBox (self);
        !          2021: }
        !          2022: 
        !          2023: void SP_func_killbox (edict_t *ent)
        !          2024: {
        !          2025:        gi.setmodel (ent, ent->model);
        !          2026:        ent->use = use_killbox;
        !          2027:        ent->svflags = SVF_NOCLIENT;
        !          2028: }
        !          2029: 
        !          2030: 
        !          2031: /*QUAKED rotating_light (0 .5 .8) (-8 -8 -8) (8 8 8) START_OFF ALARM
        !          2032: "health"       if set, the light may be killed.
        !          2033: */
        !          2034: 
        !          2035: // RAFAEL 
        !          2036: // note to self
        !          2037: // the lights will take damage from explosions
        !          2038: // this could leave a player in total darkness very bad
        !          2039:  
        !          2040: #define START_OFF      1
        !          2041: 
        !          2042: void rotating_light_alarm (edict_t *self)
        !          2043: {
        !          2044:        if (self->spawnflags & START_OFF)
        !          2045:        {
        !          2046:                self->think = NULL;
        !          2047:                self->nextthink = 0;    
        !          2048:        }
        !          2049:        else
        !          2050:        {
        !          2051:                gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
        !          2052:                self->nextthink = level.time + 1;
        !          2053:        }
        !          2054: }
        !          2055: 
        !          2056: void rotating_light_killed (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
        !          2057: {
        !          2058:        
        !          2059:        gi.WriteByte (svc_temp_entity);
        !          2060:        gi.WriteByte (TE_WELDING_SPARKS);
        !          2061:        gi.WriteByte (30);
        !          2062:        gi.WritePosition (self->s.origin);
        !          2063:        gi.WriteDir (vec3_origin);
        !          2064:        gi.WriteByte (0xe0 + (rand()&7));
        !          2065:        gi.multicast (self->s.origin, MULTICAST_PVS);
        !          2066: 
        !          2067:        self->s.effects &= ~EF_SPINNINGLIGHTS;
        !          2068:        self->use = NULL;
        !          2069: 
        !          2070:        self->think = G_FreeEdict;      
        !          2071:        self->nextthink = level.time + 0.1;
        !          2072:        
        !          2073: }
        !          2074: 
        !          2075: static void rotating_light_use (edict_t *self, edict_t *other, edict_t *activator)
        !          2076: {
        !          2077:        if (self->spawnflags & START_OFF)
        !          2078:        {
        !          2079:                self->spawnflags &= ~START_OFF;
        !          2080:                self->s.effects |= EF_SPINNINGLIGHTS;
        !          2081: 
        !          2082:                if (self->spawnflags & 2)
        !          2083:                {
        !          2084:                        self->think = rotating_light_alarm;
        !          2085:                        self->nextthink = level.time + 0.1;
        !          2086:                }
        !          2087:        }
        !          2088:        else
        !          2089:        {
        !          2090:                self->spawnflags |= START_OFF;
        !          2091:                self->s.effects &= ~EF_SPINNINGLIGHTS;
        !          2092:        }
        !          2093: }
        !          2094:        
        !          2095: 
        !          2096: void SP_rotating_light (edict_t *self)
        !          2097: {
        !          2098: 
        !          2099:        self->movetype = MOVETYPE_STOP;
        !          2100:        self->solid = SOLID_BBOX;
        !          2101:        
        !          2102:        self->s.modelindex = gi.modelindex ("models/objects/light/tris.md2");
        !          2103:        
        !          2104:        self->s.frame = 0;
        !          2105:                
        !          2106:        self->use = rotating_light_use;
        !          2107:        
        !          2108:        if (self->spawnflags & START_OFF)
        !          2109:                self->s.effects &= ~EF_SPINNINGLIGHTS;
        !          2110:        else
        !          2111:        {
        !          2112:                self->s.effects |= EF_SPINNINGLIGHTS;
        !          2113:        }
        !          2114: 
        !          2115:        if (!self->speed)
        !          2116:                self->speed = 32;
        !          2117:        // this is a real cheap way
        !          2118:        // to set the radius of the light
        !          2119:        // self->s.frame = self->speed;
        !          2120: 
        !          2121:        if (!self->health)
        !          2122:        {
        !          2123:                self->health = 10;
        !          2124:                self->max_health = self->health;
        !          2125:                self->die = rotating_light_killed;
        !          2126:                self->takedamage = DAMAGE_YES;
        !          2127:        }
        !          2128:        else
        !          2129:        {
        !          2130:                self->max_health = self->health;
        !          2131:                self->die = rotating_light_killed;
        !          2132:                self->takedamage = DAMAGE_YES;
        !          2133:        }
        !          2134:        
        !          2135:        if (self->spawnflags & 2)
        !          2136:        {
        !          2137:                self->moveinfo.sound_start = gi.soundindex ("misc/alarm.wav");  
        !          2138:        }
        !          2139:        
        !          2140:        gi.linkentity (self);
        !          2141: 
        !          2142: }
        !          2143: 
        !          2144: 
        !          2145: /*QUAKED func_object_repair (1 .5 0) (-8 -8 -8) (8 8 8) 
        !          2146: object to be repaired.
        !          2147: The default delay is 1 second
        !          2148: "delay" the delay in seconds for spark to occur
        !          2149: */
        !          2150: 
        !          2151: void object_repair_fx (edict_t *ent)
        !          2152: {
        !          2153:  
        !          2154:  
        !          2155:        ent->nextthink = level.time + ent->delay;
        !          2156: 
        !          2157:        if (ent->health <= 100)
        !          2158:                ent->health++;
        !          2159:        else
        !          2160:        {
        !          2161:                gi.WriteByte (svc_temp_entity);
        !          2162:                gi.WriteByte (TE_WELDING_SPARKS);
        !          2163:                gi.WriteByte (10);
        !          2164:                gi.WritePosition (ent->s.origin);
        !          2165:                gi.WriteDir (vec3_origin);
        !          2166:                gi.WriteByte (0xe0 + (rand()&7));
        !          2167:                gi.multicast (ent->s.origin, MULTICAST_PVS);
        !          2168:        }
        !          2169:        
        !          2170: }
        !          2171: 
        !          2172: 
        !          2173: void object_repair_dead (edict_t *ent)
        !          2174: {
        !          2175:        G_UseTargets (ent, ent);
        !          2176:        ent->nextthink = level.time + 0.1;
        !          2177:        ent->think = object_repair_fx;
        !          2178: }
        !          2179: 
        !          2180: void object_repair_sparks (edict_t *ent)
        !          2181: {
        !          2182:  
        !          2183:        if (ent->health < 0)
        !          2184:        {
        !          2185:                ent->nextthink = level.time + 0.1;
        !          2186:                ent->think = object_repair_dead;
        !          2187:                return;
        !          2188:        }
        !          2189: 
        !          2190:        ent->nextthink = level.time + ent->delay;
        !          2191:        
        !          2192:        gi.WriteByte (svc_temp_entity);
        !          2193:        gi.WriteByte (TE_WELDING_SPARKS);
        !          2194:        gi.WriteByte (10);
        !          2195:        gi.WritePosition (ent->s.origin);
        !          2196:        gi.WriteDir (vec3_origin);
        !          2197:        gi.WriteByte (0xe0 + (rand()&7));
        !          2198:        gi.multicast (ent->s.origin, MULTICAST_PVS);
        !          2199:        
        !          2200: }
        !          2201: 
        !          2202: void SP_object_repair (edict_t *ent)
        !          2203: {
        !          2204:        ent->movetype = MOVETYPE_NONE;
        !          2205:        ent->solid = SOLID_BBOX;
        !          2206:        ent->classname = "object_repair";
        !          2207:        VectorSet (ent->mins, -8, -8, 8);
        !          2208:        VectorSet (ent->maxs, 8, 8, 8);
        !          2209:        ent->think = object_repair_sparks;
        !          2210:        ent->nextthink = level.time + 1.0;
        !          2211:        ent->health = 100;
        !          2212:        if (!ent->delay)
        !          2213:                ent->delay = 1.0;
        !          2214:        
        !          2215: }
        !          2216: 

unix.superglobalmegacorp.com

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