Annotation of quake2/rogue/g_newweap.c, revision 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.