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

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

unix.superglobalmegacorp.com

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