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

1.1       root        1: #include "g_local.h"
                      2: 
                      3: #define INCLUDE_ETF_RIFLE              1
                      4: #define INCLUDE_PROX                   1
                      5: //#define INCLUDE_FLAMETHROWER 1
                      6: //#define INCLUDE_INCENDIARY           1
                      7: #define INCLUDE_NUKE                   1
                      8: #define INCLUDE_MELEE                  1
                      9: #define INCLUDE_TESLA                  1
                     10: #define INCLUDE_BEAMS                  1
                     11: 
                     12: extern void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed);
                     13: extern void hurt_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
                     14: extern void droptofloor (edict_t *ent);
                     15: extern void Grenade_Explode (edict_t *ent);
                     16: 
                     17: extern void drawbbox (edict_t *ent);
                     18: 
                     19: #ifdef INCLUDE_ETF_RIFLE
                     20: /*
                     21: ========================
                     22: fire_flechette
                     23: ========================
                     24: */
                     25: void flechette_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
                     26: {
                     27:        vec3_t          dir;
                     28: 
                     29:        if (other == self->owner)
                     30:                return;
                     31: 
                     32:        if (surf && (surf->flags & SURF_SKY))
                     33:        {
                     34:                G_FreeEdict (self);
                     35:                return;
                     36:        }
                     37: 
                     38:        if (self->client)
                     39:                PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
                     40: 
                     41:        if (other->takedamage)
                     42:        {
                     43: //gi.dprintf("t_damage %s\n", other->classname);
                     44:                T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
                     45:                        self->dmg, self->dmg_radius, DAMAGE_NO_REG_ARMOR, MOD_ETF_RIFLE);
                     46:        }
                     47:        else
                     48:        {
                     49:                if(!plane)
                     50:                        VectorClear (dir);
                     51:                else
                     52:                        VectorScale (plane->normal, 256, dir);
                     53:                gi.WriteByte (svc_temp_entity);
                     54:                gi.WriteByte (TE_FLECHETTE);
                     55:                gi.WritePosition (self->s.origin);
                     56:                gi.WriteDir (dir);
                     57:                gi.multicast (self->s.origin, MULTICAST_PVS);
                     58: 
                     59: //             T_RadiusDamage(self, self->owner, 24, self, 48, MOD_ETF_RIFLE);
                     60:        }
                     61: 
                     62:        G_FreeEdict (self);
                     63: }
                     64: 
                     65: void fire_flechette (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int kick)
                     66: {
                     67:        edict_t *flechette;
                     68: 
                     69:        VectorNormalize (dir);
                     70: 
                     71:        flechette = G_Spawn();
                     72:        VectorCopy (start, flechette->s.origin);
                     73:        VectorCopy (start, flechette->s.old_origin);
                     74:        vectoangles2 (dir, flechette->s.angles);
                     75: 
                     76:        VectorScale (dir, speed, flechette->velocity);
                     77:        flechette->movetype = MOVETYPE_FLYMISSILE;
                     78:        flechette->clipmask = MASK_SHOT;
                     79:        flechette->solid = SOLID_BBOX;
                     80:        flechette->s.renderfx = RF_FULLBRIGHT;
                     81:        VectorClear (flechette->mins);
                     82:        VectorClear (flechette->maxs);
                     83:        
                     84:        flechette->s.modelindex = gi.modelindex ("models/proj/flechette/tris.md2");
                     85: 
                     86: //     flechette->s.sound = gi.soundindex ("");                        // FIXME - correct sound!
                     87:        flechette->owner = self;
                     88:        flechette->touch = flechette_touch;
                     89:        flechette->nextthink = level.time + 8000/speed;
                     90:        flechette->think = G_FreeEdict;
                     91:        flechette->dmg = damage;
                     92:        flechette->dmg_radius = kick;
                     93: 
                     94:        gi.linkentity (flechette);
                     95:        
                     96:        if (self->client)
                     97:                check_dodge (self, flechette->s.origin, dir, speed);
                     98: }
                     99: #endif
                    100: 
                    101: // **************************
                    102: // PROX
                    103: // **************************
                    104: 
                    105: #ifdef INCLUDE_PROX
                    106: #define PROX_TIME_TO_LIVE      45              // 45, 30, 15, 10
                    107: #define PROX_TIME_DELAY                0.5
                    108: #define PROX_BOUND_SIZE                96
                    109: #define PROX_DAMAGE_RADIUS     192
                    110: #define PROX_HEALTH                    20
                    111: #define        PROX_DAMAGE                     90
                    112: 
                    113: //===============
                    114: //===============
                    115: void Prox_Explode (edict_t *ent)
                    116: {
                    117:        vec3_t          origin;
                    118:        edict_t         *owner;
                    119: 
                    120: // free the trigger field
                    121: 
                    122:        //PMM - changed teammaster to "mover" .. owner of the field is the prox
                    123:        if(ent->teamchain && ent->teamchain->owner == ent)
                    124:                G_FreeEdict(ent->teamchain);
                    125: 
                    126:        owner = ent;
                    127:        if(ent->teammaster)
                    128:        {
                    129:                owner = ent->teammaster;
                    130:                PlayerNoise(owner, ent->s.origin, PNOISE_IMPACT);
                    131:        }
                    132: 
                    133:        // play quad sound if appopriate
                    134:        if (ent->dmg > PROX_DAMAGE)
                    135:                gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
                    136: 
                    137:        ent->takedamage = DAMAGE_NO;
                    138:        T_RadiusDamage(ent, owner, ent->dmg, ent, PROX_DAMAGE_RADIUS, MOD_PROX);
                    139: 
                    140:        VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
                    141:        gi.WriteByte (svc_temp_entity);
                    142:        if (ent->groundentity)
                    143:                gi.WriteByte (TE_GRENADE_EXPLOSION);
                    144:        else
                    145:                gi.WriteByte (TE_ROCKET_EXPLOSION);
                    146:        gi.WritePosition (origin);
                    147:        gi.multicast (ent->s.origin, MULTICAST_PVS);
                    148: 
                    149:        G_FreeEdict (ent);
                    150: }
                    151: 
                    152: //===============
                    153: //===============
                    154: void prox_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
                    155: {
                    156: //     gi.dprintf("prox_die\n");
                    157:        // if set off by another prox, delay a little (chained explosions)
                    158:        if (strcmp(inflictor->classname, "prox"))
                    159:        {
                    160:                self->takedamage = DAMAGE_NO;
                    161:                Prox_Explode(self);
                    162:        }
                    163:        else
                    164:        {
                    165:                self->takedamage = DAMAGE_NO;
                    166:                self->think = Prox_Explode;
                    167:                self->nextthink = level.time + FRAMETIME;
                    168:        }
                    169: }
                    170: 
                    171: //===============
                    172: //===============
                    173: void Prox_Field_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
                    174: {
                    175:        edict_t *prox;
                    176: 
                    177:        if (!(other->svflags & SVF_MONSTER) && !other->client)
                    178:                return;
                    179: 
                    180:        // trigger the prox mine if it's still there, and still mine.
                    181:        prox = ent->owner;
                    182: 
                    183:        if (other == prox) // don't set self off
                    184:                return;
                    185: 
                    186:        if (prox->think == Prox_Explode) // we're set to blow!
                    187:        {
                    188: //             if ((g_showlogic) && (g_showlogic->value))
                    189: //                     gi.dprintf ("%f - prox already gone off!\n", level.time);
                    190:                return;
                    191:        }
                    192: 
                    193:        if(prox->teamchain == ent)
                    194:        {
                    195:                gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/proxwarn.wav"), 1, ATTN_NORM, 0);
                    196:                prox->think = Prox_Explode;
                    197:                prox->nextthink = level.time + PROX_TIME_DELAY;
                    198:                return;
                    199:        }
                    200: 
                    201:        ent->solid = SOLID_NOT;
                    202:        G_FreeEdict(ent);
                    203: }
                    204: 
                    205: //===============
                    206: //===============
                    207: void prox_seek (edict_t *ent)
                    208: {
                    209:        if(level.time > ent->wait)
                    210:        {
                    211:                Prox_Explode(ent);
                    212:        }
                    213:        else
                    214:        {
                    215:                ent->s.frame++;
                    216:                if(ent->s.frame > 13)
                    217:                        ent->s.frame = 9;
                    218:                ent->think = prox_seek;
                    219:                ent->nextthink = level.time + 0.1;
                    220:        }
                    221: }
                    222: 
                    223: //===============
                    224: //===============
                    225: void prox_open (edict_t *ent)
                    226: {
                    227:        edict_t *search;
                    228: 
                    229:        search = NULL;
                    230: //     gi.dprintf("prox_open %d\n", ent->s.frame);     
                    231: //     gi.dprintf("%f\n", ent->velocity[2]);
                    232:        if(ent->s.frame == 9)   // end of opening animation
                    233:        {
                    234:                // set the owner to NULL so the owner can shoot it, etc.  needs to be done here so the owner
                    235:                // doesn't get stuck on it while it's opening if fired at point blank wall
                    236:                ent->owner = NULL;
                    237:                if(ent->teamchain)
                    238:                        ent->teamchain->touch = Prox_Field_Touch;
                    239:                while ((search = findradius(search, ent->s.origin, PROX_DAMAGE_RADIUS+10)) != NULL)
                    240:                {
                    241:                        if (!search->classname)                 // tag token and other weird shit
                    242:                                continue;
                    243: 
                    244: //                     if (!search->takedamage)
                    245: //                             continue;
                    246:                        // if it's a monster or player with health > 0
                    247:                        // or it's a player start point
                    248:                        // and we can see it
                    249:                        // blow up
                    250:                        if (
                    251:                                (
                    252:                                        (((search->svflags & SVF_MONSTER) || (search->client)) && (search->health > 0)) || 
                    253:                                        (
                    254:                                                (deathmatch->value) && 
                    255:                                                (
                    256:                                                (!strcmp(search->classname, "info_player_deathmatch")) ||
                    257:                                                (!strcmp(search->classname, "info_player_start")) ||
                    258:                                                (!strcmp(search->classname, "info_player_coop"))
                    259:                                                )
                    260:                                        )
                    261:                                ) 
                    262:                                && (visible (search, ent))
                    263:                           )
                    264:                        {
                    265:                                gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/proxwarn.wav"), 1, ATTN_NORM, 0);
                    266:                                Prox_Explode (ent);
                    267:                                return;
                    268:                        }
                    269:                }
                    270: 
                    271:                switch (ent->dmg/PROX_DAMAGE)
                    272:                {
                    273:                case 1:
                    274:                        ent->wait = level.time + PROX_TIME_TO_LIVE;
                    275:                        break;
                    276:                case 2:
                    277:                        ent->wait = level.time + 30;
                    278:                        break;
                    279:                case 4:
                    280:                        ent->wait = level.time + 15;
                    281:                        break;
                    282:                case 8:
                    283:                        ent->wait = level.time + 10;
                    284:                        break;
                    285:                default:
                    286:                        if ((g_showlogic) && (g_showlogic->value))
                    287:                                gi.dprintf ("prox with unknown multiplier %d!\n", ent->dmg/PROX_DAMAGE);
                    288:                        ent->wait = level.time + PROX_TIME_TO_LIVE;
                    289:                        break;
                    290:                }
                    291: 
                    292: //             ent->wait = level.time + PROX_TIME_TO_LIVE;
                    293:                ent->think = prox_seek;
                    294:                ent->nextthink = level.time + 0.2;
                    295:        }
                    296:        else
                    297:        {
                    298:                ent->s.frame++;
                    299:                ent->think = prox_open;
                    300:                ent->nextthink = level.time + 0.05;     
                    301:        }
                    302: }
                    303: 
                    304: //===============
                    305: //===============
                    306: void prox_land (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
                    307: {
                    308:        edict_t *field;
                    309:        vec3_t  dir;
                    310:        vec3_t  forward, right, up;
                    311:        int             makeslave = 0;
                    312:        int             movetype = MOVETYPE_NONE;
                    313:        int             stick_ok = 0;
                    314:        vec3_t  land_point;
                    315: 
                    316:        // must turn off owner so owner can shoot it and set it off
                    317:        // moved to prox_open so owner can get away from it if fired at pointblank range into
                    318:        // wall
                    319: //     ent->owner = NULL;
                    320: 
                    321: //     if ((g_showlogic) && (g_showlogic->value))
                    322: //             gi.dprintf ("land - %2.2f %2.2f %2.2f\n", ent->velocity[0], ent->velocity[1], ent->velocity[2]);
                    323: 
                    324:        if (surf && (surf->flags & SURF_SKY))
                    325:        {
                    326:                G_FreeEdict(ent);
                    327:                return;
                    328:        }
                    329: 
                    330:        if (plane->normal)
                    331:        {
                    332:                VectorMA (ent->s.origin, -10.0, plane->normal, land_point);
                    333:                if (gi.pointcontents (land_point) & (CONTENTS_SLIME|CONTENTS_LAVA))
                    334:                {
                    335:                        Prox_Explode (ent);
                    336:                        return;
                    337:                }
                    338:        }
                    339: 
                    340:        if ((other->svflags & SVF_MONSTER) || other->client || (other->svflags & SVF_DAMAGEABLE))
                    341:        {
                    342:                if(other != ent->teammaster)
                    343:                        Prox_Explode(ent);
                    344: 
                    345:                return;
                    346:        }
                    347: 
                    348: #define STOP_EPSILON   0.1
                    349: 
                    350:        else if (other != world)
                    351:        {
                    352:                //Here we need to check to see if we can stop on this entity.
                    353:                //Note that plane can be NULL
                    354: 
                    355:                //PMM - code stolen from g_phys (ClipVelocity)
                    356:                vec3_t out;
                    357:                float backoff, change;
                    358:                int i;
                    359: 
                    360:                if (!plane->normal) // this happens if you hit a point object, maybe other cases
                    361:                {
                    362:                        // Since we can't tell what's going to happen, just blow up
                    363:                        if ((g_showlogic) && (g_showlogic->value))
                    364:                                gi.dprintf ("bad normal for surface, exploding!\n");
                    365: 
                    366:                        Prox_Explode(ent);
                    367:                        return;
                    368:                }
                    369: 
                    370:                if ((other->movetype == MOVETYPE_PUSH) && (plane->normal[2] > 0.7))
                    371:                        stick_ok = 1;
                    372:                else
                    373:                        stick_ok = 0;
                    374: 
                    375:                backoff = DotProduct (ent->velocity, plane->normal) * 1.5;
                    376:                for (i=0 ; i<3 ; i++)
                    377:                {
                    378:                        change = plane->normal[i]*backoff;
                    379:                        out[i] = ent->velocity[i] - change;
                    380:                        if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
                    381:                                out[i] = 0;
                    382:                }
                    383: 
                    384:                if (out[2] > 60)
                    385:                        return;
                    386: 
                    387:                movetype = MOVETYPE_BOUNCE;
                    388: 
                    389:                // if we're here, we're going to stop on an entity
                    390:                if (stick_ok)
                    391:                { // it's a happy entity
                    392:                        VectorCopy (vec3_origin, ent->velocity);
                    393:                        VectorCopy (vec3_origin, ent->avelocity);
                    394:                }
                    395:                else // no-stick.  teflon time
                    396:                {
                    397:                        if (plane->normal[2] > 0.7)
                    398:                        {
                    399:                                if ((g_showlogic) && (g_showlogic->value))
                    400:                                        gi.dprintf ("stuck on entity, blowing up!\n");
                    401: 
                    402:                                Prox_Explode(ent);
                    403:                                return;
                    404:                        }
                    405:                        return;
                    406:                }
                    407:        }
                    408:        else if (other->s.modelindex != 1)
                    409:                return;
                    410: 
                    411:        vectoangles2 (plane->normal, dir);
                    412:        AngleVectors (dir, forward, right, up);
                    413: 
                    414:        if (gi.pointcontents (ent->s.origin) & (CONTENTS_LAVA|CONTENTS_SLIME))
                    415:        {
                    416:                Prox_Explode (ent);
                    417:                return;
                    418:        }
                    419: 
                    420:        field = G_Spawn();
                    421: 
                    422:        VectorCopy (ent->s.origin, field->s.origin);
                    423:        VectorClear(field->velocity);
                    424:        VectorClear(field->avelocity);
                    425:        VectorSet(field->mins, -PROX_BOUND_SIZE, -PROX_BOUND_SIZE, -PROX_BOUND_SIZE);
                    426:        VectorSet(field->maxs, PROX_BOUND_SIZE, PROX_BOUND_SIZE, PROX_BOUND_SIZE);
                    427:        field->movetype = MOVETYPE_NONE;
                    428:        field->solid = SOLID_TRIGGER;
                    429:        field->owner = ent;
                    430:        field->classname = "prox_field";
                    431:        field->teammaster = ent;
                    432:        gi.linkentity (field);
                    433: 
                    434:        VectorClear(ent->velocity);
                    435:        VectorClear(ent->avelocity);
                    436:        // rotate to vertical
                    437:        dir[PITCH] = dir[PITCH] + 90;
                    438:        VectorCopy (dir, ent->s.angles);
                    439:        ent->takedamage = DAMAGE_AIM;
                    440:        ent->movetype = movetype;               // either bounce or none, depending on whether we stuck to something
                    441:        ent->die = prox_die;
                    442:        ent->teamchain = field;
                    443:        ent->health = PROX_HEALTH;
                    444:        ent->nextthink = level.time + 0.05;
                    445:        ent->think = prox_open;
                    446:        ent->touch = NULL;
                    447:        ent->solid = SOLID_BBOX;
                    448:        // record who we're attached to
                    449: //     ent->teammaster = other;
                    450: 
                    451:        gi.linkentity(ent);
                    452: }
                    453: 
                    454: //===============
                    455: //===============
                    456: void fire_prox (edict_t *self, vec3_t start, vec3_t aimdir, int damage_multiplier, int speed)
                    457: {
                    458:        edict_t *prox;
                    459:        vec3_t  dir;
                    460:        vec3_t  forward, right, up;
                    461: 
                    462:        vectoangles2 (aimdir, dir);
                    463:        AngleVectors (dir, forward, right, up);
                    464: 
                    465: //     if ((g_showlogic) && (g_showlogic->value))
                    466: //             gi.dprintf ("start %s    aim %s   speed %d\n", vtos(start), vtos(aimdir), speed);
                    467:        prox = G_Spawn();
                    468:        VectorCopy (start, prox->s.origin);
                    469:        VectorScale (aimdir, speed, prox->velocity);
                    470:        VectorMA (prox->velocity, 200 + crandom() * 10.0, up, prox->velocity);
                    471:        VectorMA (prox->velocity, crandom() * 10.0, right, prox->velocity);
                    472:        VectorCopy (dir, prox->s.angles);
                    473:        prox->s.angles[PITCH]-=90;
                    474:        prox->movetype = MOVETYPE_BOUNCE;
                    475:        prox->solid = SOLID_BBOX; 
                    476:        prox->s.effects |= EF_GRENADE;
                    477:        prox->clipmask = MASK_SHOT|CONTENTS_LAVA|CONTENTS_SLIME;
                    478:        prox->s.renderfx |= RF_IR_VISIBLE;
                    479:        //FIXME - this needs to be bigger.  Has other effects, though.  Maybe have to change origin to compensate
                    480:        // so it sinks in correctly.  Also in lavacheck, might have to up the distance
                    481:        VectorSet (prox->mins, -6, -6, -6);
                    482:        VectorSet (prox->maxs, 6, 6, 6);
                    483:        prox->s.modelindex = gi.modelindex ("models/weapons/g_prox/tris.md2");
                    484:        prox->owner = self;
                    485:        prox->teammaster = self;
                    486:        prox->touch = prox_land;
                    487: //     prox->nextthink = level.time + PROX_TIME_TO_LIVE;
                    488:        prox->think = Prox_Explode;
                    489:        prox->dmg = PROX_DAMAGE*damage_multiplier;
                    490:        prox->classname = "prox";
                    491:        prox->svflags |= SVF_DAMAGEABLE;
                    492:        prox->flags |= FL_MECHANICAL;
                    493: 
                    494:        switch (damage_multiplier)
                    495:        {
                    496:        case 1:
                    497:                prox->nextthink = level.time + PROX_TIME_TO_LIVE;
                    498:                break;
                    499:        case 2:
                    500:                prox->nextthink = level.time + 30;
                    501:                break;
                    502:        case 4:
                    503:                prox->nextthink = level.time + 15;
                    504:                break;
                    505:        case 8:
                    506:                prox->nextthink = level.time + 10;
                    507:                break;
                    508:        default:
                    509:                if ((g_showlogic) && (g_showlogic->value))
                    510:                        gi.dprintf ("prox with unknown multiplier %d!\n", damage_multiplier);
                    511:                prox->nextthink = level.time + PROX_TIME_TO_LIVE;
                    512:                break;
                    513:        }
                    514: 
                    515:        gi.linkentity (prox);
                    516: }
                    517: #endif
                    518: 
                    519: // *************************
                    520: // FLAMETHROWER
                    521: // *************************
                    522: 
                    523: #ifdef INCLUDE_FLAMETHROWER
                    524: #define FLAMETHROWER_RADIUS            8
                    525: 
                    526: void fire_remove (edict_t *ent)
                    527: {
                    528:        if(ent == ent->owner->teamchain)
                    529:                ent->owner->teamchain = NULL;
                    530: 
                    531:        G_FreeEdict(ent);       
                    532: }
                    533: 
                    534: void fire_flame (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed)
                    535: {
                    536:        edict_t *flame;
                    537:        vec3_t  dir;
                    538:        vec3_t  forward, right, up;
                    539: 
                    540:        vectoangles2 (aimdir, dir);
                    541:        AngleVectors (dir, forward, right, up);
                    542: 
                    543:        flame = G_Spawn();
                    544: 
                    545:        // the origin is the first control point, put it speed forward.
                    546:        VectorMA(start, speed, forward, flame->s.origin);
                    547: 
                    548:        // record that velocity
                    549:        VectorScale (aimdir, speed, flame->velocity);
                    550: 
                    551:        VectorCopy (dir, flame->s.angles);
                    552:        flame->movetype = MOVETYPE_NONE;
                    553:        flame->solid = SOLID_NOT;
                    554: 
                    555:        VectorSet(flame->mins, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS);
                    556:        VectorSet(flame->maxs, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS);
                    557: 
                    558:        flame->s.sound = gi.soundindex ("weapons/flame.wav");
                    559:        flame->owner = self;
                    560:        flame->dmg = damage;
                    561:        flame->classname = "flame";
                    562: 
                    563:        // clear control points and velocities
                    564:        VectorCopy (flame->s.origin, flame->flameinfo.pos1);
                    565:        VectorCopy (flame->velocity, flame->flameinfo.vel1);
                    566:        VectorCopy (flame->s.origin, flame->flameinfo.pos2);
                    567:        VectorCopy (flame->velocity, flame->flameinfo.vel2);
                    568:        VectorCopy (flame->s.origin, flame->flameinfo.pos3);
                    569:        VectorCopy (flame->velocity, flame->flameinfo.vel3);
                    570:        VectorCopy (flame->s.origin, flame->flameinfo.pos4);
                    571: 
                    572:        // hook flame stream to owner
                    573:        self->teamchain = flame;
                    574: 
                    575:        gi.linkentity (flame);
                    576: }
                    577: 
                    578: // fixme - change to use start location, not entity origin
                    579: void fire_maintain (edict_t *ent, edict_t *flame, vec3_t start, vec3_t aimdir, int damage, int speed)
                    580: {
                    581:        trace_t tr;
                    582: 
                    583:        // move the control points out the appropriate direction and velocity
                    584:        VectorAdd(flame->flameinfo.pos3, flame->flameinfo.vel3, flame->flameinfo.pos4);
                    585:        VectorAdd(flame->flameinfo.pos2, flame->flameinfo.vel2, flame->flameinfo.pos3);
                    586:        VectorAdd(flame->flameinfo.pos1, flame->flameinfo.vel1, flame->flameinfo.pos2);
                    587:        VectorAdd(flame->s.origin,               flame->velocity,       flame->flameinfo.pos1);
                    588: 
                    589:        // move the velocities for the control points
                    590:        VectorCopy(flame->flameinfo.vel2, flame->flameinfo.vel3);
                    591:        VectorCopy(flame->flameinfo.vel1, flame->flameinfo.vel2);
                    592:        VectorCopy(flame->velocity,               flame->flameinfo.vel1);
                    593: 
                    594:        // set velocity and location for new control point 0.
                    595:        VectorMA(start, speed, aimdir, flame->s.origin);
                    596:        VectorScale(aimdir, speed, flame->velocity);
                    597:        
                    598:        //
                    599:        // does it hit a wall? if so, when?
                    600:        //
                    601: 
                    602:        // player fire point to flame origin.
                    603:        tr = gi.trace(start, flame->mins, flame->maxs,
                    604:                                        flame->s.origin, flame, MASK_SHOT);
                    605:        if(tr.fraction == 1.0)
                    606:        {
                    607:                // origin to point 1
                    608:                tr = gi.trace(flame->s.origin, flame->mins, flame->maxs,
                    609:                                                flame->flameinfo.pos1, flame, MASK_SHOT);
                    610:                if(tr.fraction == 1.0)
                    611:                {
                    612:                        // point 1 to point 2
                    613:                        tr = gi.trace(flame->flameinfo.pos1, flame->mins, flame->maxs,
                    614:                                                        flame->flameinfo.pos2, flame, MASK_SHOT);
                    615:                        if(tr.fraction == 1.0)
                    616:                        {
                    617:                                // point 2 to point 3
                    618:                                tr = gi.trace(flame->flameinfo.pos2, flame->mins, flame->maxs,
                    619:                                                        flame->flameinfo.pos3, flame, MASK_SHOT);
                    620:                                if(tr.fraction == 1.0)
                    621:                                {
                    622:                                        // point 3 to point 4, point 3 valid
                    623:                                        tr = gi.trace(flame->flameinfo.pos3, flame->mins, flame->maxs,
                    624:                                                                flame->flameinfo.pos4, flame, MASK_SHOT);
                    625:                                        if(tr.fraction < 1.0) // point 4 blocked
                    626:                                        {
                    627:                                                VectorCopy(tr.endpos, flame->flameinfo.pos4);
                    628:                                        }
                    629:                                }
                    630:                                else    // point 3 blocked, point 2 valid
                    631:                                {
                    632:                                        VectorCopy(flame->flameinfo.vel2, flame->flameinfo.vel3);
                    633:                                        VectorCopy(tr.endpos, flame->flameinfo.pos3);
                    634:                                        VectorCopy(tr.endpos, flame->flameinfo.pos4);
                    635:                                }
                    636:                        }
                    637:                        else    // point 2 blocked, point 1 valid
                    638:                        {
                    639:                                VectorCopy(flame->flameinfo.vel1, flame->flameinfo.vel2);
                    640:                                VectorCopy(flame->flameinfo.vel1, flame->flameinfo.vel3);
                    641:                                VectorCopy(tr.endpos, flame->flameinfo.pos2);
                    642:                                VectorCopy(tr.endpos, flame->flameinfo.pos3);
                    643:                                VectorCopy(tr.endpos, flame->flameinfo.pos4);
                    644:                        }
                    645:                }
                    646:                else    // point 1 blocked, origin valid
                    647:                {
                    648:                        VectorCopy(flame->velocity, flame->flameinfo.vel1);
                    649:                        VectorCopy(flame->velocity, flame->flameinfo.vel2);
                    650:                        VectorCopy(flame->velocity, flame->flameinfo.vel3);
                    651:                        VectorCopy(tr.endpos, flame->flameinfo.pos1);
                    652:                        VectorCopy(tr.endpos, flame->flameinfo.pos2);
                    653:                        VectorCopy(tr.endpos, flame->flameinfo.pos3);
                    654:                        VectorCopy(tr.endpos, flame->flameinfo.pos4);
                    655:                }
                    656:        }
                    657:        else // origin blocked!
                    658:        {
                    659: //             gi.dprintf("point 2 blocked\n");
                    660:                VectorCopy(flame->velocity, flame->flameinfo.vel1);
                    661:                VectorCopy(flame->velocity, flame->flameinfo.vel2);
                    662:                VectorCopy(flame->velocity, flame->flameinfo.vel3);
                    663:                VectorCopy(tr.endpos, flame->s.origin);
                    664:                VectorCopy(tr.endpos, flame->flameinfo.pos1);
                    665:                VectorCopy(tr.endpos, flame->flameinfo.pos2);
                    666:                VectorCopy(tr.endpos, flame->flameinfo.pos3);
                    667:                VectorCopy(tr.endpos, flame->flameinfo.pos4);
                    668:        }
                    669:        
                    670:        if(tr.fraction < 1.0 && tr.ent->takedamage)
                    671:        {
                    672:                T_Damage (tr.ent, flame, ent, flame->velocity, tr.endpos, tr.plane.normal, 
                    673:                                        damage, 0, DAMAGE_NO_KNOCKBACK | DAMAGE_ENERGY | DAMAGE_FIRE);
                    674:        }
                    675: 
                    676:        gi.linkentity(flame);
                    677: 
                    678:        gi.WriteByte (svc_temp_entity);
                    679:        gi.WriteByte (TE_FLAME);
                    680:        gi.WriteShort(ent - g_edicts);
                    681:        gi.WriteShort(6);
                    682:        gi.WritePosition (start);
                    683:        gi.WritePosition (flame->s.origin);
                    684:        gi.WritePosition (flame->flameinfo.pos1);
                    685:        gi.WritePosition (flame->flameinfo.pos2);
                    686:        gi.WritePosition (flame->flameinfo.pos3);
                    687:        gi.WritePosition (flame->flameinfo.pos4);
                    688:        gi.multicast (flame->s.origin, MULTICAST_PVS);
                    689: }
                    690: 
                    691: /*QUAKED trap_flameshooter (1 0 0) (-8 -8 -8) (8 8 8)
                    692: */
                    693: #define FLAMESHOOTER_VELOCITY                  50
                    694: #define FLAMESHOOTER_DAMAGE                            20
                    695: #define FLAMESHOOTER_BURST_VELOCITY            300
                    696: #define FLAMESHOOTER_BURST_DAMAGE              30
                    697: 
                    698: //#define FLAMESHOOTER_PUFF    1
                    699: #define FLAMESHOOTER_STREAM    1
                    700: 
                    701: void flameshooter_think (edict_t *self)
                    702: {
                    703:        vec3_t  forward, right, up;
                    704:        edict_t *flame;
                    705:        
                    706:        if(self->delay)
                    707:        {
                    708:                if(self->teamchain)
                    709:                        fire_remove (self->teamchain);
                    710:                return;
                    711:        }
                    712: 
                    713:        self->s.angles[1] += self->speed;
                    714:        if(self->s.angles[1] > 135 || self->s.angles[1] < 45)
                    715:                self->speed = -self->speed;
                    716:                 
                    717:        AngleVectors (self->s.angles, forward, right, up);
                    718: 
                    719: #ifdef FLAMESHOOTER_STREAM
                    720:        flame = self->teamchain;
                    721:        if(!self->teamchain)
                    722:                fire_flame (self, self->s.origin, forward, FLAMESHOOTER_DAMAGE, FLAMESHOOTER_VELOCITY);
                    723:        else
                    724:                fire_maintain (self, flame, self->s.origin, forward, FLAMESHOOTER_DAMAGE, FLAMESHOOTER_VELOCITY);
                    725: 
                    726:        self->think = flameshooter_think;
                    727:        self->nextthink = level.time + 0.05;
                    728: #else
                    729:        fire_burst (self, self->s.origin, forward, FLAMESHOOTER_BURST_DAMAGE, FLAMESHOOTER_BURST_VELOCITY);
                    730:        
                    731:        self->think = flameshooter_think;
                    732:        self->nextthink = level.time + 0.1;
                    733: #endif
                    734: }
                    735: 
                    736: void flameshooter_use (edict_t *self, edict_t *other, edict_t *activator)
                    737: {
                    738:        if(self->delay)
                    739:        {
                    740:                self->delay = 0;
                    741:                self->think = flameshooter_think;
                    742:                self->nextthink = level.time + 0.1;
                    743:        }       
                    744:        else
                    745:                self->delay = 1;
                    746: }
                    747: 
                    748: void SP_trap_flameshooter(edict_t *self)
                    749: {
                    750:        vec3_t  tempAngles;
                    751: 
                    752:        self->solid = SOLID_NOT;
                    753:        self->movetype = MOVETYPE_NONE;
                    754: 
                    755:        self->delay = 0;
                    756: 
                    757:        self->use =     flameshooter_use;
                    758:        if(self->delay == 0)
                    759:        {
                    760:                self->think = flameshooter_think;
                    761:                self->nextthink = level.time  + 0.1;
                    762:        }
                    763: 
                    764: //     self->flags |= FL_NOCLIENT;
                    765: 
                    766:        self->speed = 10;
                    767: 
                    768: //     self->speed = 0;        // FIXME this stops the spraying
                    769: 
                    770:        VectorCopy(self->s.angles, tempAngles);
                    771: 
                    772:        if (!VectorCompare(self->s.angles, vec3_origin))
                    773:                G_SetMovedir (self->s.angles, self->movedir);
                    774: 
                    775:        VectorCopy(tempAngles, self->s.angles);
                    776: 
                    777: //     gi.setmodel (self, self->model);
                    778:        gi.linkentity (self);
                    779: }
                    780: 
                    781: // *************************
                    782: // fire_burst
                    783: // *************************
                    784: 
                    785: #define FLAME_BURST_MAX_SIZE   64
                    786: #define FLAME_BURST_FRAMES             20
                    787: #define FLAME_BURST_MIDPOINT   10
                    788: 
                    789: void fire_burst_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
                    790: {
                    791:        int             powerunits;
                    792:        int             damage, radius;
                    793:        vec3_t  origin;
                    794:        
                    795:        if (surf && (surf->flags & SURF_SKY))
                    796:        {
                    797: //             gi.dprintf("Hit sky. Removed\n");
                    798:                G_FreeEdict (ent);
                    799:                return;
                    800:        }
                    801: 
                    802:        if(other == ent->owner || ent == other)
                    803:                return;
                    804: 
                    805:        // don't let flame puffs blow each other up
                    806:        if(other->classname && !strcmp(other->classname, ent->classname))
                    807:                return;
                    808: 
                    809:        if(ent->waterlevel)
                    810:        {
                    811: //             gi.dprintf("Hit water. Removed\n");
                    812:                G_FreeEdict(ent);               
                    813:        }
                    814: 
                    815:        if(!(other->svflags & SVF_MONSTER) && !other->client)
                    816:        {
                    817:                powerunits = FLAME_BURST_FRAMES - ent->s.frame;
                    818:                damage = powerunits * 6;
                    819:                radius = powerunits * 4;
                    820: 
                    821: //             T_RadiusDamage (inflictor, attacker, damage, ignore, radius)
                    822:                T_RadiusDamage(ent, ent->owner, damage, ent, radius, DAMAGE_FIRE);
                    823: 
                    824: //             gi.dprintf("Hit world: %d pts, %d rad\n", damage, radius);
                    825: 
                    826:                // calculate position for the explosion entity
                    827:                VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
                    828: 
                    829:                gi.WriteByte (svc_temp_entity);
                    830:                gi.WriteByte (TE_PLAIN_EXPLOSION);
                    831:                gi.WritePosition (origin);
                    832:                gi.multicast (ent->s.origin, MULTICAST_PVS);
                    833: 
                    834:                G_FreeEdict (ent);
                    835:        }
                    836: }
                    837: 
                    838: void fire_burst_think (edict_t *self)
                    839: {
                    840:        int     current_radius;
                    841: 
                    842:        if(self->waterlevel)
                    843:        {
                    844:                G_FreeEdict(self);
                    845:                return;
                    846:        }
                    847: 
                    848:        self->s.frame++;
                    849:        if(self->s.frame >= FLAME_BURST_FRAMES)
                    850:        {
                    851:                G_FreeEdict(self);
                    852:                return;
                    853:        }
                    854: 
                    855:        else if(self->s.frame < FLAME_BURST_MIDPOINT)
                    856:        {
                    857:                current_radius = (FLAME_BURST_MAX_SIZE / FLAME_BURST_MIDPOINT) * self->s.frame;
                    858:        }
                    859:        else
                    860:        {
                    861:                current_radius = (FLAME_BURST_MAX_SIZE / FLAME_BURST_MIDPOINT) * (FLAME_BURST_FRAMES - self->s.frame);
                    862:        }
                    863: 
                    864:        if(self->s.frame == 3)
                    865:                self->s.skinnum = 1;
                    866:        else if (self->s.frame == 7)
                    867:                self->s.skinnum = 2;
                    868:        else if (self->s.frame == 10)
                    869:                self->s.skinnum = 3;
                    870:        else if (self->s.frame == 13)
                    871:                self->s.skinnum = 4;
                    872:        else if (self->s.frame == 16)
                    873:                self->s.skinnum = 5;
                    874:        else if (self->s.frame == 19)
                    875:                self->s.skinnum = 6;
                    876: 
                    877:        if(current_radius < 8)
                    878:                current_radius = 8;
                    879:        else if(current_radius > FLAME_BURST_MAX_SIZE)
                    880:                current_radius = FLAME_BURST_MAX_SIZE;
                    881: 
                    882:        T_RadiusDamage(self, self->owner, self->dmg, self, current_radius, DAMAGE_FIRE);
                    883: 
                    884:        self->think = fire_burst_think;
                    885:        self->nextthink = level.time + 0.1;
                    886: }
                    887: 
                    888: void fire_burst (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed)
                    889: {
                    890:        edict_t *flame;
                    891:        vec3_t  dir;
                    892:        vec3_t  baseVel;
                    893:        vec3_t  forward, right, up;
                    894: 
                    895:        vectoangles2 (aimdir, dir);
                    896:        AngleVectors (dir, forward, right, up);
                    897: 
                    898:        flame = G_Spawn();
                    899:        VectorCopy(start, flame->s.origin);
                    900: //     VectorScale (aimdir, speed, flame->velocity);
                    901: 
                    902:        // scale down so only 30% of player's velocity is taken into account.
                    903:        VectorScale (self->velocity, 0.3, baseVel);
                    904:        VectorMA(baseVel, speed, aimdir, flame->velocity);
                    905: 
                    906:        VectorCopy (dir, flame->s.angles);
                    907:        flame->movetype = MOVETYPE_FLY;
                    908:        flame->solid = SOLID_TRIGGER;
                    909: 
                    910:        VectorSet(flame->mins, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS);
                    911:        VectorSet(flame->maxs, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS);
                    912: 
                    913:        flame->s.sound = gi.soundindex ("weapons/flame.wav");
                    914:        flame->s.modelindex = gi.modelindex ("models/projectiles/puff/tris.md2");
                    915:        flame->owner = self;
                    916:        flame->touch = fire_burst_touch;
                    917:        flame->think = fire_burst_think;
                    918:        flame->nextthink = level.time + 0.1;
                    919:        flame->dmg = damage;
                    920:        flame->classname = "flameburst";
                    921:        flame->s.effects = EF_FIRE_PUFF;
                    922: 
                    923:        gi.linkentity (flame);
                    924: }
                    925: #endif
                    926: 
                    927: // *************************
                    928: //     INCENDIARY GRENADES
                    929: // *************************
                    930: 
                    931: #ifdef INCLUDE_INCENDIARY
                    932: void FireThink (edict_t *ent)
                    933: {
                    934:        if(level.time > ent->wait)
                    935:                G_FreeEdict(ent);
                    936:        else
                    937:        {
                    938:                ent->s.frame++;
                    939:                if(ent->s.frame>10)
                    940:                        ent->s.frame = 0;
                    941:                ent->nextthink = level.time + 0.05;
                    942:                ent->think = FireThink;
                    943:        }
                    944: }
                    945: 
                    946: #define FIRE_HEIGHT            64
                    947: #define FIRE_RADIUS            64
                    948: #define FIRE_DAMAGE            3
                    949: #define FIRE_DURATION  15
                    950: 
                    951: edict_t *StartFire(edict_t *fireOwner, vec3_t fireOrigin, float fireDuration, float fireDamage)
                    952: {
                    953:        edict_t *fire;
                    954: 
                    955:        fire = G_Spawn();
                    956:        VectorCopy (fireOrigin, fire->s.origin);
                    957:        fire->movetype = MOVETYPE_TOSS;
                    958:        fire->solid = SOLID_TRIGGER;
                    959:        VectorSet(fire->mins, -FIRE_RADIUS, -FIRE_RADIUS, 0);
                    960:        VectorSet(fire->maxs, FIRE_RADIUS, FIRE_RADIUS, FIRE_HEIGHT);
                    961: 
                    962:        fire->s.sound = gi.soundindex ("weapons/incend.wav");
                    963:        fire->s.modelindex = gi.modelindex ("models/objects/fire/tris.md2");
                    964: 
                    965:        fire->owner = fireOwner;
                    966:        fire->touch = hurt_touch;
                    967:        fire->nextthink = level.time + 0.05;
                    968:        fire->wait = level.time + fireDuration;
                    969:        fire->think = FireThink;
                    970: //     fire->nextthink = level.time + fireDuration;
                    971: //     fire->think = G_FreeEdict;
                    972:        fire->dmg = fireDamage;
                    973:        fire->classname = "incendiary_fire";
                    974:        
                    975:        gi.linkentity (fire);
                    976: 
                    977: //     gi.sound (fire, CHAN_VOICE, gi.soundindex ("weapons/incend.wav"), 1, ATTN_NORM, 0);
                    978:        return fire;
                    979: }
                    980: 
                    981: static void Incendiary_Explode (edict_t *ent)
                    982: {
                    983:        vec3_t          origin;
                    984: 
                    985:        if (ent->owner->client)
                    986:                PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
                    987: 
                    988:        //FIXME: if we are onground then raise our Z just a bit since we are a point?
                    989:        T_RadiusDamage(ent, ent->owner, ent->dmg, NULL, ent->dmg_radius, DAMAGE_FIRE);
                    990: 
                    991:        VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
                    992:        gi.WriteByte (svc_temp_entity);
                    993:        if (ent->groundentity)
                    994:                gi.WriteByte (TE_GRENADE_EXPLOSION);
                    995:        else
                    996:                gi.WriteByte (TE_ROCKET_EXPLOSION);
                    997:        gi.WritePosition (origin);
                    998:        gi.multicast (ent->s.origin, MULTICAST_PVS);
                    999: 
                   1000:        StartFire(ent->owner, ent->s.origin, FIRE_DURATION, FIRE_DAMAGE);
                   1001: 
                   1002:        G_FreeEdict (ent);
                   1003: 
                   1004: }
                   1005: 
                   1006: static void Incendiary_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
                   1007: {
                   1008:        if (other == ent->owner)
                   1009:                return;
                   1010: 
                   1011:        if (surf && (surf->flags & SURF_SKY))
                   1012:        {
                   1013:                G_FreeEdict (ent);
                   1014:                return;
                   1015:        }
                   1016: 
                   1017:        if (!(other->svflags & SVF_MONSTER) && !(ent->client))
                   1018: //     if (!other->takedamage)
                   1019:        {
                   1020:                if (ent->spawnflags & 1)
                   1021:                {
                   1022:                        if (random() > 0.5)
                   1023:                                gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
                   1024:                        else
                   1025:                                gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
                   1026:                }
                   1027:                else
                   1028:                {
                   1029:                        if (random() > 0.5)
                   1030:                                gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
                   1031:                        else
                   1032:                                gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb2b.wav"), 1, ATTN_NORM, 0);
                   1033:                }
                   1034:                return;
                   1035:        }
                   1036: 
                   1037:        Incendiary_Explode (ent);
                   1038: }
                   1039: 
                   1040: void fire_incendiary_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
                   1041: {
                   1042:        edict_t *grenade;
                   1043:        vec3_t  dir;
                   1044:        vec3_t  forward, right, up;
                   1045: 
                   1046:        vectoangles2 (aimdir, dir);
                   1047:        AngleVectors (dir, forward, right, up);
                   1048: 
                   1049:        grenade = G_Spawn();
                   1050:        VectorCopy (start, grenade->s.origin);
                   1051:        VectorScale (aimdir, speed, grenade->velocity);
                   1052:        VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
                   1053:        VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
                   1054:        VectorSet (grenade->avelocity, 300, 300, 300);
                   1055:        grenade->movetype = MOVETYPE_BOUNCE;
                   1056:        grenade->clipmask = MASK_SHOT;
                   1057:        grenade->solid = SOLID_BBOX;
                   1058:        grenade->s.effects |= EF_GRENADE;
                   1059: //     if (self->client)
                   1060: //             grenade->s.effects &= ~EF_TELEPORT;
                   1061:        VectorClear (grenade->mins);
                   1062:        VectorClear (grenade->maxs);
                   1063:        grenade->s.modelindex = gi.modelindex ("models/projectiles/incend/tris.md2");
                   1064:        grenade->owner = self;
                   1065:        grenade->touch = Incendiary_Touch;
                   1066:        grenade->nextthink = level.time + timer;
                   1067:        grenade->think = Incendiary_Explode;
                   1068:        grenade->dmg = damage;
                   1069:        grenade->dmg_radius = damage_radius;
                   1070:        grenade->classname = "incendiary_grenade";
                   1071: 
                   1072:        gi.linkentity (grenade);
                   1073: }
                   1074: #endif
                   1075: 
                   1076: // *************************
                   1077: // MELEE WEAPONS
                   1078: // *************************
                   1079: 
                   1080: #ifdef INCLUDE_MELEE
                   1081: void fire_player_melee (edict_t *self, vec3_t start, vec3_t aim, int reach, int damage, int kick, int quiet, int mod)
                   1082: {
                   1083:        vec3_t          forward, right, up;
                   1084:        vec3_t          v;
                   1085:        vec3_t          point;
                   1086:        trace_t         tr;
                   1087: 
                   1088:        vectoangles2 (aim, v);
                   1089:        AngleVectors (v, forward, right, up);
                   1090:        VectorNormalize (forward);
                   1091:        VectorMA( start, reach, forward, point);
                   1092: 
                   1093:        //see if the hit connects
                   1094:        tr = gi.trace(start, NULL, NULL, point, self, MASK_SHOT);
                   1095:        if(tr.fraction ==  1.0)
                   1096:        {
                   1097:                if(!quiet)
                   1098:                        gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/swish.wav"), 1, ATTN_NORM, 0);
                   1099:                //FIXME some sound here?
                   1100:                return;
                   1101:        }
                   1102: 
                   1103:        if(tr.ent->takedamage == DAMAGE_YES || tr.ent->takedamage == DAMAGE_AIM)
                   1104:        {
                   1105:                // pull the player forward if you do damage
                   1106:                VectorMA(self->velocity, 75, forward, self->velocity);
                   1107:                VectorMA(self->velocity, 75, up, self->velocity);
                   1108: 
                   1109:                // do the damage
                   1110:                // FIXME - make the damage appear at right spot and direction
                   1111:                if(mod == MOD_CHAINFIST)
                   1112:                        T_Damage (tr.ent, self, self, vec3_origin, tr.ent->s.origin, vec3_origin, damage, kick/2, 
                   1113:                                                DAMAGE_DESTROY_ARMOR | DAMAGE_NO_KNOCKBACK, mod);
                   1114:                else
                   1115:                        T_Damage (tr.ent, self, self, vec3_origin, tr.ent->s.origin, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, mod);
                   1116: 
                   1117:                if(!quiet)
                   1118:                        gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/meatht.wav"), 1, ATTN_NORM, 0);
                   1119:        }
                   1120:        else
                   1121:        {
                   1122:                if(!quiet)
                   1123:                        gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/tink1.wav"), 1, ATTN_NORM, 0);
                   1124: 
                   1125:                VectorScale (tr.plane.normal, 256, point);
                   1126:                gi.WriteByte (svc_temp_entity);
                   1127:                gi.WriteByte (TE_GUNSHOT);
                   1128:                gi.WritePosition (tr.endpos);
                   1129:                gi.WriteDir (point);
                   1130:                gi.multicast (tr.endpos, MULTICAST_PVS);
                   1131:        }
                   1132: }
                   1133: #endif
                   1134: 
                   1135: // *************************
                   1136: // NUKE 
                   1137: // *************************
                   1138: 
                   1139: #ifdef INCLUDE_NUKE
                   1140: #define        NUKE_DELAY                      4
                   1141: #define NUKE_TIME_TO_LIVE      6
                   1142: //#define NUKE_TIME_TO_LIVE    40
                   1143: #define NUKE_RADIUS                    512
                   1144: #define NUKE_DAMAGE                    400
                   1145: #define        NUKE_QUAKE_TIME         3
                   1146: #define NUKE_QUAKE_STRENGTH    100
                   1147: 
                   1148: void Nuke_Quake (edict_t *self)
                   1149: {
                   1150:        int             i;
                   1151:        edict_t *e;
                   1152: 
                   1153:        if (self->last_move_time < level.time)
                   1154:        {
                   1155:                gi.positioned_sound (self->s.origin, self, CHAN_AUTO, self->noise_index, 0.75, ATTN_NONE, 0);
                   1156:                self->last_move_time = level.time + 0.5;
                   1157:        }
                   1158: 
                   1159:        for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++)
                   1160:        {
                   1161:                if (!e->inuse)
                   1162:                        continue;
                   1163:                if (!e->client)
                   1164:                        continue;
                   1165:                if (!e->groundentity)
                   1166:                        continue;
                   1167: 
                   1168:                e->groundentity = NULL;
                   1169:                e->velocity[0] += crandom()* 150;
                   1170:                e->velocity[1] += crandom()* 150;
                   1171:                e->velocity[2] = self->speed * (100.0 / e->mass);
                   1172:        }
                   1173: 
                   1174:        if (level.time < self->timestamp)
                   1175:                self->nextthink = level.time + FRAMETIME;
                   1176:        else
                   1177:                G_FreeEdict (self);
                   1178: }
                   1179: 
                   1180: 
                   1181: static void Nuke_Explode (edict_t *ent)
                   1182: {
                   1183: //     vec3_t          origin;
                   1184: 
                   1185: //     nuke_framenum = level.framenum + 20;
                   1186: 
                   1187:        if (ent->teammaster->client)
                   1188:                PlayerNoise(ent->teammaster, ent->s.origin, PNOISE_IMPACT);
                   1189: 
                   1190:        T_RadiusNukeDamage(ent, ent->teammaster, ent->dmg, ent, ent->dmg_radius, MOD_NUKE);
                   1191: 
                   1192: //     VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
                   1193:        if (ent->dmg > NUKE_DAMAGE)
                   1194:                gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
                   1195: 
                   1196:        gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/grenlx1a.wav"), 1, ATTN_NONE, 0);
                   1197: /*
                   1198:        gi.WriteByte (svc_temp_entity);
                   1199:        if (ent->groundentity)
                   1200:                gi.WriteByte (TE_GRENADE_EXPLOSION);
                   1201:        else
                   1202:                gi.WriteByte (TE_ROCKET_EXPLOSION);
                   1203:        gi.WritePosition (origin);
                   1204:        gi.multicast (ent->s.origin, MULTICAST_PVS);
                   1205: */
                   1206: 
                   1207:        //      BecomeExplosion1(ent);
                   1208: 
                   1209:        gi.WriteByte (svc_temp_entity);
                   1210:        gi.WriteByte (TE_EXPLOSION1_BIG);
                   1211:        gi.WritePosition (ent->s.origin);
                   1212:        gi.multicast (ent->s.origin, MULTICAST_PVS);
                   1213: 
                   1214:        gi.WriteByte (svc_temp_entity);
                   1215:        gi.WriteByte (TE_NUKEBLAST);
                   1216:        gi.WritePosition (ent->s.origin);
                   1217:        gi.multicast (ent->s.origin, MULTICAST_ALL);
                   1218: 
                   1219:        // become a quake
                   1220:        ent->svflags |= SVF_NOCLIENT;
                   1221:        ent->noise_index = gi.soundindex ("world/rumble.wav");
                   1222:        ent->think = Nuke_Quake;
                   1223:        ent->speed = NUKE_QUAKE_STRENGTH;
                   1224:        ent->timestamp = level.time + NUKE_QUAKE_TIME;
                   1225:        ent->nextthink = level.time + FRAMETIME;
                   1226:        ent->last_move_time = 0;
                   1227: }
                   1228: 
                   1229: void nuke_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
                   1230: {
                   1231:        self->takedamage = DAMAGE_NO;
                   1232:        if ((attacker) && !(strcmp(attacker->classname, "nuke")))
                   1233:        {
                   1234:                if ((g_showlogic) && (g_showlogic->value))
                   1235:                        gi.dprintf ("nuke nuked by a nuke, not nuking\n");
                   1236:                G_FreeEdict (self);     
                   1237:                return;
                   1238:        }
                   1239:        Nuke_Explode(self);
                   1240: }
                   1241: 
                   1242: void Nuke_Think(edict_t *ent)
                   1243: {
                   1244:        float attenuation, default_atten = 1.8;
                   1245:        int             damage_multiplier, muzzleflash;
                   1246: 
                   1247: //     gi.dprintf ("player range: %2.2f    damage radius: %2.2f\n", realrange (ent, ent->teammaster), ent->dmg_radius*2);
                   1248: 
                   1249:        damage_multiplier = ent->dmg/NUKE_DAMAGE;
                   1250:        switch (damage_multiplier)
                   1251:        {
                   1252:        case 1:
                   1253:                attenuation = default_atten/1.4;
                   1254:                muzzleflash = MZ_NUKE1;
                   1255:                break;
                   1256:        case 2:
                   1257:                attenuation = default_atten/2.0;
                   1258:                muzzleflash = MZ_NUKE2;
                   1259:                break;
                   1260:        case 4:
                   1261:                attenuation = default_atten/3.0;
                   1262:                muzzleflash = MZ_NUKE4;
                   1263:                break;
                   1264:        case 8:
                   1265:                attenuation = default_atten/5.0;
                   1266:                muzzleflash = MZ_NUKE8;
                   1267:                break;
                   1268:        default:
                   1269:                if ((g_showlogic) && (g_showlogic->value))
                   1270:                        gi.dprintf ("default attenuation used for nuke!\n");
                   1271:                attenuation = default_atten;
                   1272:                muzzleflash = MZ_NUKE1;
                   1273:                break;
                   1274:        }
                   1275: 
                   1276:        if(ent->wait < level.time)
                   1277:                Nuke_Explode(ent);
                   1278:        else if (level.time >= (ent->wait - NUKE_TIME_TO_LIVE))
                   1279:        {
                   1280:                ent->s.frame++;
                   1281: //             if ((g_showlogic) && (g_showlogic->value))
                   1282: //                     gi.dprintf ("nuke frame %d\n", ent->s.frame);
                   1283:                if(ent->s.frame > 11)
                   1284:                        ent->s.frame = 6;
                   1285: 
                   1286:                if (gi.pointcontents (ent->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA))
                   1287:                {
                   1288:                        Nuke_Explode (ent);
                   1289:                        return;
                   1290:                }
                   1291: 
                   1292:                ent->think = Nuke_Think;
                   1293:                ent->nextthink = level.time + 0.1;
                   1294:                ent->health = 1;
                   1295:                ent->owner = NULL;
                   1296: 
                   1297:                gi.WriteByte (svc_muzzleflash);
                   1298:                gi.WriteShort (ent-g_edicts);
                   1299:                gi.WriteByte (muzzleflash);
                   1300:                gi.multicast (ent->s.origin, MULTICAST_PVS);
                   1301: 
                   1302:                if (ent->timestamp <= level.time)
                   1303:                {
                   1304: /*                     gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn.wav"), 1, ATTN_NORM, 0);
                   1305:                        ent->timestamp += 10.0;
                   1306:                }
                   1307: */             
                   1308: 
                   1309:                        if ((ent->wait - level.time) <= (NUKE_TIME_TO_LIVE/2.0))
                   1310:                        {
                   1311: //                             ent->s.sound = gi.soundindex ("weapons/nukewarn.wav");
                   1312: //                             gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
                   1313:                                gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0);
                   1314: //                             gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
                   1315: //                             gi.dprintf ("time %2.2f\n", ent->wait-level.time);
                   1316:                                ent->timestamp = level.time + 0.3;
                   1317:                        }
                   1318:                        else
                   1319:                        {
                   1320:                                gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0);
                   1321: //                             gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
                   1322:                                ent->timestamp = level.time + 0.5;
                   1323: //                             gi.dprintf ("time %2.2f\n", ent->wait-level.time);
                   1324:                        }
                   1325:                }
                   1326:        }
                   1327:        else
                   1328:        {
                   1329:                if (ent->timestamp <= level.time)
                   1330:                {
                   1331:                        gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0);
                   1332: //                     gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
                   1333: //                             gi.dprintf ("time %2.2f\n", ent->wait-level.time);
                   1334:                        ent->timestamp = level.time + 1.0;
                   1335:                }
                   1336:                ent->nextthink = level.time + FRAMETIME;
                   1337:        }
                   1338: }
                   1339: 
                   1340: void nuke_bounce (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
                   1341: {
                   1342:        if (random() > 0.5)
                   1343:                gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
                   1344:        else
                   1345:                gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
                   1346: }
                   1347: 
                   1348: 
                   1349: extern byte P_DamageModifier(edict_t *ent);
                   1350: 
                   1351: void fire_nuke (edict_t *self, vec3_t start, vec3_t aimdir, int speed)
                   1352: {
                   1353:        edict_t *nuke;
                   1354:        vec3_t  dir;
                   1355:        vec3_t  forward, right, up;
                   1356:        int             damage_modifier;
                   1357: 
                   1358:        damage_modifier = (int) P_DamageModifier (self);
                   1359: 
                   1360:        vectoangles2 (aimdir, dir);
                   1361:        AngleVectors (dir, forward, right, up);
                   1362: 
                   1363:        nuke = G_Spawn();
                   1364:        VectorCopy (start, nuke->s.origin);
                   1365:        VectorScale (aimdir, speed, nuke->velocity);
                   1366: 
                   1367:        VectorMA (nuke->velocity, 200 + crandom() * 10.0, up, nuke->velocity);
                   1368:        VectorMA (nuke->velocity, crandom() * 10.0, right, nuke->velocity);
                   1369:        VectorClear (nuke->avelocity);
                   1370:        VectorClear (nuke->s.angles);
                   1371:        nuke->movetype = MOVETYPE_BOUNCE;
                   1372:        nuke->clipmask = MASK_SHOT;
                   1373:        nuke->solid = SOLID_BBOX;
                   1374:        nuke->s.effects |= EF_GRENADE;
                   1375:        nuke->s.renderfx |= RF_IR_VISIBLE;
                   1376:        VectorSet (nuke->mins, -8, -8, 0);
                   1377:        VectorSet (nuke->maxs, 8, 8, 16);
                   1378:        nuke->s.modelindex = gi.modelindex ("models/weapons/g_nuke/tris.md2");
                   1379:        nuke->owner = self;
                   1380:        nuke->teammaster = self;
                   1381:        nuke->nextthink = level.time + FRAMETIME;
                   1382:        nuke->wait = level.time + NUKE_DELAY + NUKE_TIME_TO_LIVE;
                   1383:        nuke->think = Nuke_Think;
                   1384:        nuke->touch = nuke_bounce;
                   1385: 
                   1386:        nuke->health = 10000;
                   1387:        nuke->takedamage = DAMAGE_YES;
                   1388:        nuke->svflags |= SVF_DAMAGEABLE;
                   1389:        nuke->dmg = NUKE_DAMAGE * damage_modifier;
                   1390:        if (damage_modifier == 1)
                   1391:                nuke->dmg_radius = NUKE_RADIUS;
                   1392:        else
                   1393:                nuke->dmg_radius = NUKE_RADIUS + NUKE_RADIUS*(0.25*(float)damage_modifier);
                   1394:        // this yields 1.0, 1.5, 2.0, 3.0 times radius
                   1395:        
                   1396:        if ((g_showlogic) && (g_showlogic->value))
                   1397:                gi.dprintf ("nuke modifier = %d, damage = %d, radius = %f\n", damage_modifier, nuke->dmg, nuke->dmg_radius);
                   1398: 
                   1399:        nuke->classname = "nuke";
                   1400:        nuke->die = nuke_die;
                   1401: 
                   1402:        gi.linkentity (nuke);
                   1403: }
                   1404: #endif
                   1405: 
                   1406: // *************************
                   1407: // TESLA
                   1408: // *************************
                   1409: 
                   1410: #ifdef INCLUDE_TESLA
                   1411: #define TESLA_TIME_TO_LIVE             30
                   1412: #define TESLA_DAMAGE_RADIUS            128
                   1413: #define TESLA_DAMAGE                   3               // 3
                   1414: #define TESLA_KNOCKBACK                        8
                   1415: 
                   1416: #define        TESLA_ACTIVATE_TIME             3
                   1417: 
                   1418: #define TESLA_EXPLOSION_DAMAGE_MULT            50              // this is the amount the damage is multiplied by for underwater explosions
                   1419: #define        TESLA_EXPLOSION_RADIUS                  200
                   1420: 
                   1421: void tesla_remove (edict_t *self)
                   1422: {
                   1423:        edict_t         *cur, *next;
                   1424: 
                   1425:        self->takedamage = DAMAGE_NO;
                   1426:        if(self->teamchain)
                   1427:        {
                   1428:                cur = self->teamchain;
                   1429:                while(cur)
                   1430:                {
                   1431:                        next = cur->teamchain;
                   1432:                        G_FreeEdict ( cur );
                   1433:                        cur = next;
                   1434:                }
                   1435:        }
                   1436:        else if (self->air_finished)
                   1437:                gi.dprintf ("tesla without a field!\n");
                   1438: 
                   1439:        self->owner = self->teammaster; // Going away, set the owner correctly.
                   1440:        // PGM - grenade explode does damage to self->enemy
                   1441:        self->enemy = NULL;
                   1442: 
                   1443:        // play quad sound if quadded and an underwater explosion
                   1444:        if ((self->dmg_radius) && (self->dmg > (TESLA_DAMAGE*TESLA_EXPLOSION_DAMAGE_MULT)))
                   1445:                gi.sound(self, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
                   1446: 
                   1447:        Grenade_Explode(self);
                   1448: }
                   1449: 
                   1450: void tesla_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
                   1451: {
                   1452: //     gi.dprintf("tesla killed\n");
                   1453:        tesla_remove(self);
                   1454: }
                   1455: 
                   1456: void tesla_blow (edict_t *self)
                   1457: {
                   1458: //     T_RadiusDamage(self, self->owner, TESLA_EXPLOSION_DAMAGE, NULL, TESLA_EXPLOSION_RADIUS, MOD_TESLA);
                   1459:        self->dmg = self->dmg * TESLA_EXPLOSION_DAMAGE_MULT;
                   1460:        self->dmg_radius = TESLA_EXPLOSION_RADIUS;
                   1461:        tesla_remove(self);
                   1462: }
                   1463: 
                   1464: 
                   1465: void tesla_zap (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
                   1466: {
                   1467: }
                   1468: 
                   1469: void tesla_think_active (edict_t *self)
                   1470: {
                   1471:        int             i,num;
                   1472:        edict_t *touch[MAX_EDICTS], *hit;
                   1473:        vec3_t  dir, start;
                   1474:        trace_t tr;
                   1475:        
                   1476:        if(level.time > self->air_finished)
                   1477:        {
                   1478:                tesla_remove(self);
                   1479:                return;
                   1480:        }
                   1481: 
                   1482:        VectorCopy(self->s.origin, start);
                   1483:        start[2] += 16;
                   1484: 
                   1485:        num = gi.BoxEdicts(self->teamchain->absmin, self->teamchain->absmax, touch, MAX_EDICTS, AREA_SOLID);
                   1486:        for(i=0;i<num;i++)
                   1487:        {
                   1488:                hit=touch[i];
                   1489:                if(!hit->inuse)
                   1490:                        continue;
                   1491:                if(hit == self)
                   1492:                        continue;
                   1493:                if(hit->health < 1)
                   1494:                        continue;
                   1495:                // don't hit clients in single-player or coop
                   1496:                if(hit->client)
                   1497:                        if (coop->value || !deathmatch->value)
                   1498:                                continue;
                   1499:                if(!(hit->svflags & (SVF_MONSTER | SVF_DAMAGEABLE)) && !hit->client)
                   1500:                        continue;
                   1501:        
                   1502:                tr = gi.trace(start, vec3_origin, vec3_origin, hit->s.origin, self, MASK_SHOT);
                   1503:                if(tr.fraction==1 || tr.ent==hit)// || tr.ent->client || (tr.ent->svflags & (SVF_MONSTER | SVF_DAMAGEABLE)))
                   1504:                {
                   1505:                        VectorSubtract(hit->s.origin, start, dir);
                   1506:                        
                   1507:                        // PMM - play quad sound if it's above the "normal" damage
                   1508:                        if (self->dmg > TESLA_DAMAGE)
                   1509:                                gi.sound(self, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
                   1510: 
                   1511:                        // PGM - don't do knockback to walking monsters
                   1512:                        if((hit->svflags & SVF_MONSTER) && !(hit->flags & (FL_FLY|FL_SWIM)))
                   1513:                                T_Damage (hit, self, self->teammaster, dir, tr.endpos, tr.plane.normal,
                   1514:                                        self->dmg, 0, 0, MOD_TESLA);
                   1515:                        else
                   1516:                                T_Damage (hit, self, self->teammaster, dir, tr.endpos, tr.plane.normal,
                   1517:                                        self->dmg, TESLA_KNOCKBACK, 0, MOD_TESLA);
                   1518: 
                   1519:                        gi.WriteByte (svc_temp_entity);
                   1520:                        gi.WriteByte (TE_LIGHTNING);
                   1521:                        gi.WriteShort (hit - g_edicts);                 // destination entity
                   1522:                        gi.WriteShort (self - g_edicts);                // source entity
                   1523:                        gi.WritePosition (tr.endpos);
                   1524:                        gi.WritePosition (start);
                   1525:                        gi.multicast (start, MULTICAST_PVS);
                   1526:                }
                   1527:        }
                   1528: 
                   1529:        if(self->inuse)
                   1530:        {
                   1531:                self->think = tesla_think_active;
                   1532:                self->nextthink = level.time + FRAMETIME;
                   1533:        }
                   1534: }
                   1535: 
                   1536: void tesla_activate (edict_t *self)
                   1537: {
                   1538:        edict_t         *trigger;
                   1539:        edict_t         *search;
                   1540: 
                   1541:        if (gi.pointcontents (self->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_WATER))
                   1542:        {
                   1543:                tesla_blow (self);
                   1544:                return;
                   1545:        }
                   1546: 
                   1547:        // only check for spawn points in deathmatch
                   1548:        if (deathmatch->value)
                   1549:        {
                   1550:                search = NULL;
                   1551:                while ((search = findradius(search, self->s.origin, 1.5*TESLA_DAMAGE_RADIUS)) != NULL)
                   1552:                {
                   1553:                        //if (!search->takedamage)
                   1554:                        //      continue;
                   1555:                        // if it's a monster or player with health > 0
                   1556:                        // or it's a deathmatch start point
                   1557:                        // and we can see it
                   1558:                        // blow up
                   1559:                        if(search->classname)
                   1560:                        {
                   1561:                                if (   ( (!strcmp(search->classname, "info_player_deathmatch"))
                   1562:                                        || (!strcmp(search->classname, "info_player_start"))
                   1563:                                        || (!strcmp(search->classname, "info_player_coop"))
                   1564:                                        || (!strcmp(search->classname, "misc_teleporter_dest"))
                   1565:                                        ) 
                   1566:                                        && (visible (search, self))
                   1567:                                   )
                   1568:                                {
                   1569:                                        if ((g_showlogic) && (g_showlogic->value))
                   1570:                                                gi.dprintf ("Tesla to close to %s, removing!\n", search->classname);
                   1571:                                        tesla_remove (self);
                   1572:                                        return;
                   1573:                                }
                   1574:                        }
                   1575:                }
                   1576:        }
                   1577: 
                   1578:        trigger = G_Spawn();
                   1579: //     if (trigger->nextthink)
                   1580: //     {
                   1581: //             if ((g_showlogic) && (g_showlogic->value))
                   1582: //                     gi.dprintf ("tesla_activate:  fixing nextthink\n");
                   1583: //             trigger->nextthink = 0;
                   1584: //     }
                   1585:        VectorCopy (self->s.origin, trigger->s.origin);
                   1586:        VectorSet (trigger->mins, -TESLA_DAMAGE_RADIUS, -TESLA_DAMAGE_RADIUS, self->mins[2]);
                   1587:        VectorSet (trigger->maxs, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS);
                   1588:        trigger->movetype = MOVETYPE_NONE;
                   1589:        trigger->solid = SOLID_TRIGGER;
                   1590:        trigger->owner = self;
                   1591:        trigger->touch = tesla_zap;
                   1592:        trigger->classname = "tesla trigger";
                   1593:        // doesn't need to be marked as a teamslave since the move code for bounce looks for teamchains
                   1594:        gi.linkentity (trigger);
                   1595: 
                   1596:        VectorClear (self->s.angles);
                   1597:        // clear the owner if in deathmatch
                   1598:        if (deathmatch->value)
                   1599:                self->owner = NULL;
                   1600:        self->teamchain = trigger;
                   1601:        self->think = tesla_think_active;
                   1602:        self->nextthink = level.time + FRAMETIME;
                   1603:        self->air_finished = level.time + TESLA_TIME_TO_LIVE;
                   1604: }
                   1605: 
                   1606: void tesla_think (edict_t *ent)
                   1607: {
                   1608:        if (gi.pointcontents (ent->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA))
                   1609:        {
                   1610:                tesla_remove (ent);
                   1611:                return;
                   1612:        }
                   1613:        VectorClear (ent->s.angles);
                   1614:        ent->s.frame++;
                   1615:        if(ent->s.frame > 14)
                   1616:        {
                   1617:                ent->s.frame = 14;
                   1618:                ent->think = tesla_activate;
                   1619:                ent->nextthink = level.time + 0.1;
                   1620:        }
                   1621:        else
                   1622:        {
                   1623:                if(ent->s.frame > 9)
                   1624:                {
                   1625:                        if(ent->s.frame == 10)
                   1626:                        {
                   1627:                                if (ent->owner && ent->owner->client)
                   1628:                                {
                   1629:                                        PlayerNoise(ent->owner, ent->s.origin, PNOISE_WEAPON);          // PGM
                   1630:                                }
                   1631:                                ent->s.skinnum = 1;
                   1632:                        }
                   1633:                        else if(ent->s.frame == 12)
                   1634:                                ent->s.skinnum = 2;
                   1635:                        else if(ent->s.frame == 14)
                   1636:                                ent->s.skinnum = 3;
                   1637:                }
                   1638:                ent->think = tesla_think;
                   1639:                ent->nextthink = level.time + 0.1;
                   1640:        }
                   1641: }
                   1642: 
                   1643: void tesla_lava (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
                   1644: {
                   1645:        vec3_t  land_point;
                   1646: 
                   1647:        if (plane->normal)
                   1648:        {
                   1649:                VectorMA (ent->s.origin, -20.0, plane->normal, land_point);
                   1650:                if (gi.pointcontents (land_point) & (CONTENTS_SLIME|CONTENTS_LAVA))
                   1651:                {
                   1652:                        tesla_blow (ent);
                   1653:                        return;
                   1654:                }
                   1655:        }
                   1656:        if (random() > 0.5)
                   1657:                gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
                   1658:        else
                   1659:                gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
                   1660: }
                   1661: 
                   1662: void fire_tesla (edict_t *self, vec3_t start, vec3_t aimdir, int damage_multiplier, int speed)
                   1663: {
                   1664:        edict_t *tesla;
                   1665:        vec3_t  dir;
                   1666:        vec3_t  forward, right, up;
                   1667: 
                   1668:        vectoangles2 (aimdir, dir);
                   1669:        AngleVectors (dir, forward, right, up);
                   1670: 
                   1671:        tesla = G_Spawn();
                   1672:        VectorCopy (start, tesla->s.origin);
                   1673:        VectorScale (aimdir, speed, tesla->velocity);
                   1674:        VectorMA (tesla->velocity, 200 + crandom() * 10.0, up, tesla->velocity);
                   1675:        VectorMA (tesla->velocity, crandom() * 10.0, right, tesla->velocity);
                   1676: //     VectorCopy (dir, tesla->s.angles);
                   1677:        VectorClear (tesla->s.angles);
                   1678:        tesla->movetype = MOVETYPE_BOUNCE;
                   1679:        tesla->solid = SOLID_BBOX;
                   1680:        tesla->s.effects |= EF_GRENADE;
                   1681:        tesla->s.renderfx |= RF_IR_VISIBLE;
                   1682: //     VectorClear (tesla->mins);
                   1683: //     VectorClear (tesla->maxs);
                   1684:        VectorSet (tesla->mins, -12, -12, 0);
                   1685:        VectorSet (tesla->maxs, 12, 12, 20);
                   1686:        tesla->s.modelindex = gi.modelindex ("models/weapons/g_tesla/tris.md2");
                   1687:        
                   1688:        tesla->owner = self;            // PGM - we don't want it owned by self YET.
                   1689:        tesla->teammaster = self;
                   1690: 
                   1691:        tesla->wait = level.time + TESLA_TIME_TO_LIVE;
                   1692:        tesla->think = tesla_think;
                   1693:        tesla->nextthink = level.time + TESLA_ACTIVATE_TIME;
                   1694: 
                   1695:        // blow up on contact with lava & slime code
                   1696:        tesla->touch = tesla_lava;
                   1697: 
                   1698:        if(deathmatch->value)
                   1699:                // PMM - lowered from 50 - 7/29/1998
                   1700:                tesla->health = 20;
                   1701:        else
                   1702:                tesla->health = 30;             // FIXME - change depending on skill?
                   1703: 
                   1704:        tesla->takedamage = DAMAGE_YES;
                   1705:        tesla->die = tesla_die;
                   1706:        tesla->dmg = TESLA_DAMAGE*damage_multiplier;
                   1707: //     tesla->dmg = 0;
                   1708:        tesla->classname = "tesla";
                   1709:        tesla->svflags |= SVF_DAMAGEABLE;
                   1710:        tesla->clipmask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA;
                   1711:        tesla->flags |= FL_MECHANICAL;
                   1712: 
                   1713:        gi.linkentity (tesla);
                   1714: }
                   1715: #endif
                   1716: 
                   1717: // *************************
                   1718: //  HEATBEAM
                   1719: // *************************
                   1720: 
                   1721: #ifdef INCLUDE_BEAMS
                   1722: static void fire_beams (edict_t *self, vec3_t start, vec3_t aimdir, vec3_t offset, int damage, int kick, int te_beam, int te_impact, int mod)
                   1723: {
                   1724:        trace_t         tr;
                   1725:        vec3_t          dir;
                   1726:        vec3_t          forward, right, up;
                   1727:        vec3_t          end;
                   1728:        vec3_t          water_start, endpoint;
                   1729:        qboolean        water = false, underwater = false;
                   1730:        int                     content_mask = MASK_SHOT | MASK_WATER;
                   1731:        vec3_t          beam_endpt;
                   1732: 
                   1733: //     tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT);
                   1734: //     if (!(tr.fraction < 1.0))
                   1735: //     {
                   1736:        vectoangles2 (aimdir, dir);
                   1737:        AngleVectors (dir, forward, right, up);
                   1738: 
                   1739:        VectorMA (start, 8192, forward, end);
                   1740: 
                   1741:        if (gi.pointcontents (start) & MASK_WATER)
                   1742:        {
                   1743: //             gi.dprintf ("Heat beam under water\n");
                   1744:                underwater = true;
                   1745:                VectorCopy (start, water_start);
                   1746:                content_mask &= ~MASK_WATER;
                   1747:        }
                   1748: 
                   1749:        tr = gi.trace (start, NULL, NULL, end, self, content_mask);
                   1750: 
                   1751:        // see if we hit water
                   1752:        if (tr.contents & MASK_WATER)
                   1753:        {
                   1754:                water = true;
                   1755:                VectorCopy (tr.endpos, water_start);
                   1756: 
                   1757:                if (!VectorCompare (start, tr.endpos))
                   1758:                {
                   1759:                        gi.WriteByte (svc_temp_entity);
                   1760:                        gi.WriteByte (TE_HEATBEAM_SPARKS);
                   1761: //                     gi.WriteByte (50);
                   1762:                        gi.WritePosition (water_start);
                   1763:                        gi.WriteDir (tr.plane.normal);
                   1764: //                     gi.WriteByte (8);
                   1765: //                     gi.WriteShort (60);
                   1766:                        gi.multicast (tr.endpos, MULTICAST_PVS);
                   1767:                }
                   1768:                // re-trace ignoring water this time
                   1769:                tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT);
                   1770:        }
                   1771:        VectorCopy (tr.endpos, endpoint);
                   1772: //     }
                   1773: 
                   1774:        // halve the damage if target underwater
                   1775:        if (water)
                   1776:        {
                   1777:                damage = damage /2;
                   1778:        }
                   1779: 
                   1780:        // send gun puff / flash
                   1781:        if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
                   1782:        {
                   1783:                if (tr.fraction < 1.0)
                   1784:                {
                   1785:                        if (tr.ent->takedamage)
                   1786:                        {
                   1787:                                T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_ENERGY, mod);
                   1788:                        }
                   1789:                        else
                   1790:                        {
                   1791:                                if ((!water) && (strncmp (tr.surface->name, "sky", 3)))
                   1792:                                {
                   1793:                                        // This is the truncated steam entry - uses 1+1+2 extra bytes of data
                   1794:                                        gi.WriteByte (svc_temp_entity);
                   1795:                                        gi.WriteByte (TE_HEATBEAM_STEAM);
                   1796: //                                     gi.WriteByte (20);
                   1797:                                        gi.WritePosition (tr.endpos);
                   1798:                                        gi.WriteDir (tr.plane.normal);
                   1799: //                                     gi.WriteByte (0xe0);
                   1800: //                                     gi.WriteShort (60);
                   1801:                                        gi.multicast (tr.endpos, MULTICAST_PVS);
                   1802: 
                   1803:                                        if (self->client)
                   1804:                                                PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
                   1805:                                }
                   1806:                        }
                   1807:                }
                   1808:        }
                   1809: 
                   1810:        // if went through water, determine where the end and make a bubble trail
                   1811:        if ((water) || (underwater))
                   1812:        {
                   1813:                vec3_t  pos;
                   1814: 
                   1815:                VectorSubtract (tr.endpos, water_start, dir);
                   1816:                VectorNormalize (dir);
                   1817:                VectorMA (tr.endpos, -2, dir, pos);
                   1818:                if (gi.pointcontents (pos) & MASK_WATER)
                   1819:                        VectorCopy (pos, tr.endpos);
                   1820:                else
                   1821:                        tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER);
                   1822: 
                   1823:                VectorAdd (water_start, tr.endpos, pos);
                   1824:                VectorScale (pos, 0.5, pos);
                   1825: 
                   1826:                gi.WriteByte (svc_temp_entity);
                   1827:                gi.WriteByte (TE_BUBBLETRAIL2);
                   1828: //             gi.WriteByte (8);
                   1829:                gi.WritePosition (water_start);
                   1830:                gi.WritePosition (tr.endpos);
                   1831:                gi.multicast (pos, MULTICAST_PVS);
                   1832:        }
                   1833: 
                   1834:        if ((!underwater) && (!water))
                   1835:        {
                   1836:                VectorCopy (tr.endpos, beam_endpt);
                   1837:        }
                   1838:        else
                   1839:        {
                   1840:                VectorCopy (endpoint, beam_endpt);
                   1841:        }
                   1842:        
                   1843:        gi.WriteByte (svc_temp_entity);
                   1844:        gi.WriteByte (te_beam);
                   1845:        gi.WriteShort (self - g_edicts);
                   1846:        gi.WritePosition (start);
                   1847:        gi.WritePosition (beam_endpt);
                   1848:        gi.multicast (self->s.origin, MULTICAST_ALL);
                   1849: 
                   1850: }
                   1851: 
                   1852: 
                   1853: /*
                   1854: =================
                   1855: fire_heat
                   1856: 
                   1857: Fires a single heat beam.  Zap.
                   1858: =================
                   1859: */
                   1860: void fire_heat (edict_t *self, vec3_t start, vec3_t aimdir, vec3_t offset, int damage, int kick, qboolean monster)
                   1861: {
                   1862:        if (monster)
                   1863:                fire_beams (self, start, aimdir, offset, damage, kick, TE_MONSTER_HEATBEAM, TE_HEATBEAM_SPARKS, MOD_HEATBEAM);
                   1864:        else
                   1865:                fire_beams (self, start, aimdir, offset, damage, kick, TE_HEATBEAM, TE_HEATBEAM_SPARKS, MOD_HEATBEAM);
                   1866: }
                   1867: 
                   1868: #endif
                   1869: 
                   1870: 
                   1871: // *************************
                   1872: //     BLASTER 2
                   1873: // *************************
                   1874: 
                   1875: /*
                   1876: =================
                   1877: fire_blaster2
                   1878: 
                   1879: Fires a single green blaster bolt.  Used by monsters, generally.
                   1880: =================
                   1881: */
                   1882: void blaster2_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
                   1883: {
                   1884:        int             mod;
                   1885:        int             damagestat;
                   1886: 
                   1887:        if (other == self->owner)
                   1888:                return;
                   1889: 
                   1890:        if (surf && (surf->flags & SURF_SKY))
                   1891:        {
                   1892:                G_FreeEdict (self);
                   1893:                return;
                   1894:        }
                   1895: 
                   1896:        if (self->owner && self->owner->client)
                   1897:                PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
                   1898: 
                   1899:        if (other->takedamage)
                   1900:        {
                   1901:                // the only time players will be firing blaster2 bolts will be from the 
                   1902:                // defender sphere.
                   1903:                if(self->owner->client)
                   1904:                        mod = MOD_DEFENDER_SPHERE;
                   1905:                else
                   1906:                        mod = MOD_BLASTER2;
                   1907: 
                   1908:                if (self->owner)
                   1909:                {
                   1910:                        damagestat = self->owner->takedamage;
                   1911:                        self->owner->takedamage = DAMAGE_NO;
                   1912:                        if (self->dmg >= 5)
                   1913:                                T_RadiusDamage(self, self->owner, self->dmg*3, other, self->dmg_radius, 0);
                   1914:                        T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod);
                   1915:                        self->owner->takedamage = damagestat;
                   1916:                }
                   1917:                else
                   1918:                {
                   1919:                        if (self->dmg >= 5)
                   1920:                                T_RadiusDamage(self, self->owner, self->dmg*3, other, self->dmg_radius, 0);
                   1921:                        T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod);
                   1922:                }
                   1923:        }
                   1924:        else
                   1925:        {
                   1926:                //PMM - yeowch this will get expensive
                   1927:                if (self->dmg >= 5)
                   1928:                        T_RadiusDamage(self, self->owner, self->dmg*3, self->owner, self->dmg_radius, 0);
                   1929: 
                   1930:                gi.WriteByte (svc_temp_entity);
                   1931:                gi.WriteByte (TE_BLASTER2);
                   1932:                gi.WritePosition (self->s.origin);
                   1933:                if (!plane)
                   1934:                        gi.WriteDir (vec3_origin);
                   1935:                else
                   1936:                        gi.WriteDir (plane->normal);
                   1937:                gi.multicast (self->s.origin, MULTICAST_PVS);
                   1938:        }
                   1939: 
                   1940:        G_FreeEdict (self);
                   1941: }
                   1942: 
                   1943: void fire_blaster2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper)
                   1944: {
                   1945:        edict_t *bolt;
                   1946:        trace_t tr;
                   1947: 
                   1948:        VectorNormalize (dir);
                   1949: 
                   1950:        bolt = G_Spawn();
                   1951:        VectorCopy (start, bolt->s.origin);
                   1952:        VectorCopy (start, bolt->s.old_origin);
                   1953:        vectoangles2 (dir, bolt->s.angles);
                   1954:        VectorScale (dir, speed, bolt->velocity);
                   1955:        bolt->movetype = MOVETYPE_FLYMISSILE;
                   1956:        bolt->clipmask = MASK_SHOT;
                   1957:        bolt->solid = SOLID_BBOX;
                   1958:        bolt->s.effects |= effect;
                   1959:        VectorClear (bolt->mins);
                   1960:        VectorClear (bolt->maxs);
                   1961:        
                   1962:                if (effect)
                   1963:                        bolt->s.effects |= EF_TRACKER;
                   1964:                bolt->dmg_radius = 128;
                   1965:                bolt->s.modelindex = gi.modelindex ("models/proj/laser2/tris.md2");
                   1966:                bolt->touch = blaster2_touch;
                   1967: 
                   1968:        bolt->owner = self;
                   1969:        bolt->nextthink = level.time + 2;
                   1970:        bolt->think = G_FreeEdict;
                   1971:        bolt->dmg = damage;
                   1972:        bolt->classname = "bolt";
                   1973:        gi.linkentity (bolt);
                   1974: 
                   1975:        if (self->client)
                   1976:                check_dodge (self, bolt->s.origin, dir, speed);
                   1977: 
                   1978:        tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
                   1979:        if (tr.fraction < 1.0)
                   1980:        {
                   1981:                VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
                   1982:                bolt->touch (bolt, tr.ent, NULL, NULL);
                   1983:        }
                   1984: }      
                   1985: 
                   1986: // *************************
                   1987: // tracker
                   1988: // *************************
                   1989: 
                   1990: /*
                   1991: void tracker_boom_think (edict_t *self)
                   1992: {
                   1993:        self->s.frame--;
                   1994:        if(self->s.frame < 0)
                   1995:                G_FreeEdict(self);
                   1996:        else
                   1997:                self->nextthink = level.time + 0.1;
                   1998: }
                   1999: 
                   2000: void tracker_boom_spawn (vec3_t origin)
                   2001: {
                   2002:        edict_t *boom;
                   2003: 
                   2004:        boom = G_Spawn();
                   2005:        VectorCopy (origin, boom->s.origin);
                   2006:        boom->s.modelindex = gi.modelindex ("models/items/spawngro/tris.md2");
                   2007:        boom->s.skinnum = 1;
                   2008:        boom->s.frame = 2;
                   2009:        boom->classname = "tracker boom";
                   2010:        gi.linkentity (boom);
                   2011: 
                   2012:        boom->think = tracker_boom_think;
                   2013:        boom->nextthink = level.time + 0.1;
                   2014:        //PMM
                   2015: //     boom->s.renderfx |= RF_TRANSLUCENT;
                   2016:        boom->s.effects |= EF_SPHERETRANS;
                   2017:        //pmm
                   2018: }
                   2019: */
                   2020: 
                   2021: #define TRACKER_DAMAGE_FLAGS   (DAMAGE_NO_POWER_ARMOR | DAMAGE_ENERGY | DAMAGE_NO_KNOCKBACK)
                   2022: #define TRACKER_IMPACT_FLAGS   (DAMAGE_NO_POWER_ARMOR | DAMAGE_ENERGY)
                   2023: 
                   2024: #define TRACKER_DAMAGE_TIME            0.5             // seconds
                   2025: 
                   2026: void tracker_pain_daemon_think (edict_t *self)
                   2027: {
                   2028:        static vec3_t   pain_normal = { 0, 0, 1 };
                   2029:        int                             hurt;
                   2030: 
                   2031:        if(!self->inuse)
                   2032:                return;
                   2033: 
                   2034:        if((level.time - self->timestamp) > TRACKER_DAMAGE_TIME)
                   2035:        {
                   2036:                if(!self->enemy->client)
                   2037:                        self->enemy->s.effects &= ~EF_TRACKERTRAIL;
                   2038:                G_FreeEdict (self);
                   2039:        }
                   2040:        else
                   2041:        {
                   2042:                if(self->enemy->health > 0)
                   2043:                {
                   2044: //                     gi.dprintf("ouch %x\n", self);
                   2045:                        T_Damage (self->enemy, self, self->owner, vec3_origin, self->enemy->s.origin, pain_normal,
                   2046:                                                self->dmg, 0, TRACKER_DAMAGE_FLAGS, MOD_TRACKER);
                   2047:                        
                   2048:                        // if we kill the player, we'll be removed.
                   2049:                        if(self->inuse)
                   2050:                        {
                   2051:                                // if we killed a monster, gib them.
                   2052:                                if (self->enemy->health < 1)
                   2053:                                {
                   2054:                                        if(self->enemy->gib_health)
                   2055:                                                hurt = - self->enemy->gib_health;
                   2056:                                        else
                   2057:                                                hurt = 500;
                   2058: 
                   2059: //                                     gi.dprintf("non-player killed. ensuring gib!  %d\n", hurt);
                   2060:                                        T_Damage (self->enemy, self, self->owner, vec3_origin, self->enemy->s.origin,
                   2061:                                                                pain_normal, hurt, 0, TRACKER_DAMAGE_FLAGS, MOD_TRACKER);
                   2062:                                }
                   2063: 
                   2064:                                if(self->enemy->client)
                   2065:                                        self->enemy->client->tracker_pain_framenum = level.framenum + 1;
                   2066:                                else
                   2067:                                        self->enemy->s.effects |= EF_TRACKERTRAIL;
                   2068:                                
                   2069:                                self->nextthink = level.time + FRAMETIME;
                   2070:                        }
                   2071:                }
                   2072:                else
                   2073:                {
                   2074:                        if(!self->enemy->client)
                   2075:                                self->enemy->s.effects &= ~EF_TRACKERTRAIL;
                   2076:                        G_FreeEdict (self);
                   2077:                }
                   2078:        }
                   2079: }
                   2080: 
                   2081: void tracker_pain_daemon_spawn (edict_t *owner, edict_t *enemy, int damage)
                   2082: {
                   2083:        edict_t  *daemon;
                   2084: 
                   2085:        if(enemy == NULL)
                   2086:                return;
                   2087: 
                   2088:        daemon = G_Spawn();
                   2089:        daemon->classname = "pain daemon";
                   2090:        daemon->think = tracker_pain_daemon_think;
                   2091:        daemon->nextthink = level.time + FRAMETIME;
                   2092:        daemon->timestamp = level.time;
                   2093:        daemon->owner = owner;
                   2094:        daemon->enemy = enemy;
                   2095:        daemon->dmg = damage;
                   2096: }
                   2097: 
                   2098: void tracker_explode (edict_t *self, cplane_t *plane)
                   2099: {
                   2100:        vec3_t  dir;
                   2101: 
                   2102:        if(!plane)
                   2103:                VectorClear (dir);
                   2104:        else
                   2105:                VectorScale (plane->normal, 256, dir);
                   2106: 
                   2107:        gi.WriteByte (svc_temp_entity);
                   2108:        gi.WriteByte (TE_TRACKER_EXPLOSION);
                   2109:        gi.WritePosition (self->s.origin);
                   2110:        gi.multicast (self->s.origin, MULTICAST_PVS);
                   2111: 
                   2112: //     gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/disrupthit.wav"), 1, ATTN_NORM, 0);
                   2113: //     tracker_boom_spawn(self->s.origin);
                   2114: 
                   2115:        G_FreeEdict (self);
                   2116: }
                   2117: 
                   2118: void tracker_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
                   2119: {
                   2120:        float   damagetime;
                   2121: 
                   2122:        if (other == self->owner)
                   2123:                return;
                   2124: 
                   2125:        if (surf && (surf->flags & SURF_SKY))
                   2126:        {
                   2127:                G_FreeEdict (self);
                   2128:                return;
                   2129:        }
                   2130: 
                   2131:        if (self->client)
                   2132:                PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
                   2133: 
                   2134:        if (other->takedamage)
                   2135:        {
                   2136:                if((other->svflags & SVF_MONSTER) || other->client)
                   2137:                {
                   2138:                        if(other->health > 0)           // knockback only for living creatures
                   2139:                        {
                   2140:                                // PMM - kickback was times 4 .. reduced to 3
                   2141:                                // now this does no damage, just knockback
                   2142:                                T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
                   2143:                                                        /* self->dmg */ 0, (self->dmg*3), TRACKER_IMPACT_FLAGS, MOD_TRACKER);
                   2144:                                
                   2145:                                if (!(other->flags & (FL_FLY|FL_SWIM)))
                   2146:                                        other->velocity[2] += 140;
                   2147:                                
                   2148:                                damagetime = ((float)self->dmg)*FRAMETIME;
                   2149:                                damagetime = damagetime / TRACKER_DAMAGE_TIME;
                   2150: //                             gi.dprintf ("damage is %f\n", damagetime);
                   2151: 
                   2152:                                tracker_pain_daemon_spawn (self->owner, other, (int)damagetime);
                   2153:                        }
                   2154:                        else                                            // lots of damage (almost autogib) for dead bodies
                   2155:                        {
                   2156:                                T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
                   2157:                                                        self->dmg*4, (self->dmg*3), TRACKER_IMPACT_FLAGS, MOD_TRACKER);
                   2158:                        }
                   2159:                }
                   2160:                else    // full damage in one shot for inanimate objects
                   2161:                {
                   2162:                        T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
                   2163:                                                self->dmg, (self->dmg*3), TRACKER_IMPACT_FLAGS, MOD_TRACKER);
                   2164:                }
                   2165:        }
                   2166: 
                   2167:        tracker_explode (self, plane);
                   2168:        return;
                   2169: }
                   2170: 
                   2171: void tracker_fly (edict_t *self)
                   2172: {
                   2173:        vec3_t  dest;
                   2174:        vec3_t  dir;
                   2175:        vec3_t  center;
                   2176: 
                   2177:        if ((!self->enemy) || (!self->enemy->inuse) || (self->enemy->health < 1))
                   2178:        {
                   2179:                tracker_explode (self, NULL);
                   2180:                return;
                   2181:        }
                   2182: /*
                   2183:        VectorCopy (self->enemy->s.origin, dest);
                   2184:        if(self->enemy->client)
                   2185:                dest[2] += self->enemy->viewheight;
                   2186: */
                   2187:        // PMM - try to hunt for center of enemy, if possible and not client
                   2188:        if(self->enemy->client)
                   2189:        {
                   2190:                VectorCopy (self->enemy->s.origin, dest);
                   2191:                dest[2] += self->enemy->viewheight;
                   2192:        }
                   2193:        // paranoia
                   2194:        else if (VectorCompare(self->enemy->absmin, vec3_origin) || VectorCompare(self->enemy->absmax, vec3_origin))
                   2195:        {
                   2196:                VectorCopy (self->enemy->s.origin, dest);
                   2197:        }
                   2198:        else
                   2199:        {
                   2200:                VectorMA (vec3_origin, 0.5, self->enemy->absmin, center);
                   2201:                VectorMA (center, 0.5, self->enemy->absmax, center);
                   2202:                VectorCopy (center, dest);
                   2203:        }
                   2204: 
                   2205:        VectorSubtract (dest, self->s.origin, dir);
                   2206:        VectorNormalize (dir);
                   2207:        vectoangles2 (dir, self->s.angles);
                   2208:        VectorScale (dir, self->speed, self->velocity);
                   2209:        VectorCopy(dest, self->monsterinfo.saved_goal);
                   2210: 
                   2211:        self->nextthink = level.time + 0.1;
                   2212: }
                   2213: 
                   2214: void fire_tracker (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, edict_t *enemy)
                   2215: {
                   2216:        edict_t *bolt;
                   2217:        trace_t tr;
                   2218: 
                   2219:        VectorNormalize (dir);
                   2220: 
                   2221:        bolt = G_Spawn();
                   2222:        VectorCopy (start, bolt->s.origin);
                   2223:        VectorCopy (start, bolt->s.old_origin);
                   2224:        vectoangles2 (dir, bolt->s.angles);
                   2225:        VectorScale (dir, speed, bolt->velocity);
                   2226:        bolt->movetype = MOVETYPE_FLYMISSILE;
                   2227:        bolt->clipmask = MASK_SHOT;
                   2228:        bolt->solid = SOLID_BBOX;
                   2229:        bolt->speed = speed;
                   2230:        bolt->s.effects = EF_TRACKER;
                   2231:        bolt->s.sound = gi.soundindex ("weapons/disrupt.wav");
                   2232:        VectorClear (bolt->mins);
                   2233:        VectorClear (bolt->maxs);
                   2234:        
                   2235:        bolt->s.modelindex = gi.modelindex ("models/proj/disintegrator/tris.md2");
                   2236:        bolt->touch = tracker_touch;
                   2237:        bolt->enemy = enemy;
                   2238:        bolt->owner = self;
                   2239:        bolt->dmg = damage;
                   2240:        bolt->classname = "tracker";
                   2241:        gi.linkentity (bolt);
                   2242: 
                   2243:        if(enemy)
                   2244:        {
                   2245:                bolt->nextthink = level.time + 0.1;
                   2246:                bolt->think = tracker_fly;
                   2247:        }
                   2248:        else
                   2249:        {
                   2250:                bolt->nextthink = level.time + 10;
                   2251:                bolt->think = G_FreeEdict;
                   2252:        }
                   2253: 
                   2254:        if (self->client)
                   2255:                check_dodge (self, bolt->s.origin, dir, speed);
                   2256: 
                   2257:        tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
                   2258:        if (tr.fraction < 1.0)
                   2259:        {
                   2260:                VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
                   2261:                bolt->touch (bolt, tr.ent, NULL, NULL);
                   2262:        }
                   2263: }      

unix.superglobalmegacorp.com

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