#include "g_local.h" //Damage done by grenade hitting opponent #define GREN_HIT 20 #define FIRELIFE 5 //how long a flame lasts in seconds... #define FLAMEDAMAGE 1//damage per serverframe #define FLARETIME 10 //Life of flare #define FLAMESPEEDLIMIT 300 #define SLOWMOMULT 0.5 #define RAILCHECKMAX 100 //protect from infinite looping void fire_grenade3 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius); void tempent (int te_type, vec3_t start, cplane_t *plane) { if (te_type == TE_ROCKET_EXPLOSION_WATER) { if (sv_sprite_explosions->value) { if (plane) VectorMA(start, 50, plane->normal, start); sprite_explosion (start, 2, plane); } else { gi.WriteByte (svc_temp_entity); gi.WriteByte (te_type); gi.WritePosition (start); gi.multicast (start, MULTICAST_PVS); } } else if (te_type == TE_ROCKET_EXPLOSION) { if (sv_sprite_explosions->value) { if (plane) VectorMA(start, 50, plane->normal, start); sprite_explosion (start, 1, plane); } else { gi.WriteByte (svc_temp_entity); gi.WriteByte (te_type); gi.WritePosition (start); gi.multicast (start, MULTICAST_PVS); } } else if (te_type == TE_GRENADE_EXPLOSION) { if (sv_sprite_explosions->value) { start[2]+=25; if (plane) VectorMA(start, 15, plane->normal, start); sprite_explosion (start, 0, plane); } else { gi.WriteByte (svc_temp_entity); gi.WriteByte (te_type); gi.WritePosition (start); gi.multicast (start, MULTICAST_PVS); } } else if (te_type == TE_GRENADE_EXPLOSION_WATER) { if (sv_sprite_explosions->value) { if (plane) VectorMA(start, 15, plane->normal, start); sprite_explosion (start, 3, plane); } else { gi.WriteByte (svc_temp_entity); gi.WriteByte (te_type); gi.WritePosition (start); gi.multicast (start, MULTICAST_PVS); } } } void sprite_explosionThink (edict_t *ent) { int i; ent->s.frame++; ent->nextthink = level.time + 0.005; if (ent->style==ent->s.frame) ent->s.effects = EF_PLASMA; if (ent->s.frame==ent->count) G_FreeEdict (ent); } void sprite_explosion (vec3_t start, int type, cplane_t *plane) { edict_t *boom; boom = G_Spawn(); VectorCopy (start, boom->s.origin); VectorClear (boom->velocity); boom->clipmask = MASK_ALL; boom->movetype = MOVETYPE_NONE; boom->solid = SOLID_NOT; boom->s.effects = EF_HYPERBLASTER; boom->s.renderfx = RF_TRANSLUCENT; if (type == 0) { boom->s.modelindex = gi.modelindex("sprites/s_boom.sp2"); boom->count = 3; gi.sound (boom, CHAN_WEAPON, gi.soundindex ("weapons/grenlx1a.wav"), 0.75, ATTN_IDLE, 0); boom->style = 2; } else if (type == 3) { boom->s.modelindex = gi.modelindex("sprites/s_boom.sp2"); boom->count = 3; gi.sound (boom, CHAN_WEAPON, gi.soundindex ("weapons/xpld_wat.wav"), 0.75, ATTN_IDLE, 0); boom->style = 2; } else if (type == 1) { boom->s.modelindex = gi.modelindex("sprites/s_kaboom.sp2"); boom->count = 7; gi.sound (boom, CHAN_WEAPON, gi.soundindex ("weapons/rocklx1a.wav"), 0.75, ATTN_IDLE, 0); boom->style = 4; } else if (type == 2) { boom->s.modelindex = gi.modelindex("sprites/s_kaboom.sp2"); boom->count = 7; gi.sound (boom, CHAN_WEAPON, gi.soundindex ("weapons/xpld_wat.wav"), 0.75, ATTN_IDLE, 0); boom->style = 4; } boom->s.frame = 0; boom->s.skinnum = 0; boom->nextthink = level.time + FRAMETIME; boom->think = sprite_explosionThink; gi.linkentity (boom); //MAKE BANG PARTICLES gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_STEAM); gi.WriteShort ((short int)-1); gi.WriteByte (25); //Amount gi.WritePosition (start); if (!plane) gi.WriteDir (vec3_origin); else gi.WriteDir (plane->normal); gi.WriteByte (224); //color gi.WriteShort (100); //speed gi.multicast (start, MULTICAST_PVS); //DONE } /* ================= check_dodge This is a support routine used when a client is firing a non-instant attack weapon. It checks to see if a monster's dodge function should be called. ================= */ /*static */void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed) { vec3_t end; vec3_t v; trace_t tr; float eta; // easy mode only ducks one quarter the time if (skill->value == 0) { if (random() > 0.5) return; } VectorMA (start, 8192, dir, end); tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT); if ((tr.ent) && (tr.ent->svflags & SVF_MONSTER) && (tr.ent->health > 0) && (tr.ent->monsterinfo.dodge) && infront(tr.ent, self)) { VectorSubtract (tr.endpos, start, v); eta = (VectorLength(v) - tr.ent->maxs[0]) / speed; tr.ent->monsterinfo.dodge (tr.ent, self, eta); } } /* ================= fire_hit Used for all impact (hit/punch/slash) attacks ================= */ qboolean fire_hit (edict_t *self, vec3_t aim, int damage, int kick) { trace_t tr; vec3_t forward, right, up; vec3_t v; vec3_t point; float range; vec3_t dir; //see if enemy is in range VectorSubtract (self->enemy->s.origin, self->s.origin, dir); range = VectorLength(dir); if (range > aim[0]) return false; if (aim[1] > self->mins[0] && aim[1] < self->maxs[0]) { // the hit is straight on so back the range up to the edge of their bbox range -= self->enemy->maxs[0]; } else { // this is a side hit so adjust the "right" value out to the edge of their bbox if (aim[1] < 0) aim[1] = self->enemy->mins[0]; else aim[1] = self->enemy->maxs[0]; } VectorMA (self->s.origin, range, dir, point); tr = gi.trace (self->s.origin, NULL, NULL, point, self, MASK_SHOT); if (tr.fraction < 1) { if (!tr.ent->takedamage) return false; // if it will hit any client/monster then hit the one we wanted to hit if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client)) tr.ent = self->enemy; } AngleVectors(self->s.angles, forward, right, up); VectorMA (self->s.origin, range, forward, point); VectorMA (point, aim[1], right, point); VectorMA (point, aim[2], up, point); VectorSubtract (point, self->enemy->s.origin, dir); // do the damage T_Damage (tr.ent, self, self, dir, point, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, MOD_HIT); if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) return false; // do our special form of knockback here VectorMA (self->enemy->absmin, 0.5, self->enemy->size, v); VectorSubtract (v, point, v); VectorNormalize (v); VectorMA (self->enemy->velocity, kick, v, self->enemy->velocity); if (self->enemy->velocity[2] > 0) self->enemy->groundentity = NULL; return true; } /* ================= fire_lead This is an internal support routine used for bullet/pellet based weapons. ================= */ static void fire_lead (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod) { trace_t tr; vec3_t xTarget; vec3_t xEnd; vec3_t dir; vec3_t forward, right, up; vec3_t end; float r; float u; float slow; vec3_t water_start; qboolean water = false; int content_mask = MASK_SHOT | MASK_WATER; int hscale = hspread; int vscale = vspread; tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT); if (!(tr.fraction < 1.0)) { vectoangles (aimdir, dir); AngleVectors (dir, forward, right, up); if (self->client) { if (self->client->laser_on && self->client->machinegun_shots<4) { hspread*=0.6; vspread*=0.6; } if (self->s.frame > 134) { //is crouching or dead hspread*=0.8; vspread*=0.8; } } r = crandom()*hspread; u = crandom()*vspread; VectorMA (start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); if (gi.pointcontents (start) & MASK_WATER) { water = true; VectorCopy (start, water_start); content_mask &= ~MASK_WATER; } tr = gi.trace (start, NULL, NULL, end, self, content_mask); if (!deathmatch->value&&!coop->value || sv_realbullets->value) { VectorSubtract (tr.endpos, start, xEnd); VectorNormalize (xEnd); fire_bullet_slow (self, start, xEnd, sv_maxvelocity->value, kick, damage, mod, te_impact); return; } // see if we hit water if (tr.contents & MASK_WATER) { int color; water = true; VectorCopy (tr.endpos, water_start); if (!VectorCompare (start, tr.endpos)) { if (tr.contents & CONTENTS_WATER) { if (strcmp(tr.surface->name, "*brwater") == 0) color = SPLASH_BROWN_WATER; else color = SPLASH_BLUE_WATER; } else if (tr.contents & CONTENTS_SLIME) color = SPLASH_SLIME; else if (tr.contents & CONTENTS_LAVA) color = SPLASH_LAVA; else color = SPLASH_UNKNOWN; if (color != SPLASH_UNKNOWN) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_SPLASH); gi.WriteByte (8); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.WriteByte (color); gi.multicast (tr.endpos, MULTICAST_PVS); } // change bullet's course when it enters water VectorSubtract (end, start, dir); vectoangles (dir, dir); AngleVectors (dir, forward, right, up); r = crandom()*hspread*2; u = crandom()*vspread*2; VectorMA (water_start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); } // re-trace ignoring water this time tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT); } } // send gun puff / flash if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) { if (tr.fraction < 1.0) { if (tr.ent->takedamage) { T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod); //slow em down if they're moving and on ground slow =(damage>70)?0.1:0.25; if ( (tr.ent->client)||(tr.ent->svflags & SVF_MONSTER) ) if (tr.ent->groundentity) if (tr.ent->health) VectorScale(tr.ent->velocity, slow ,tr.ent->velocity); } else { if (strncmp (tr.surface->name, "sky", 3) != 0) { gi.WriteByte (svc_temp_entity); gi.WriteByte (te_impact); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.multicast (tr.endpos, MULTICAST_PVS); BulletMark (self, &tr, mod); if (self->client) PlayerNoise(self, tr.endpos, PNOISE_IMPACT); } } } } // if went through water, determine where the end and make a bubble trail //* if ((water)&&(self->waterlevel>1)) { vec3_t pos; VectorSubtract (tr.endpos, water_start, dir); VectorNormalize (dir); VectorMA (tr.endpos, -2, dir, pos); if (gi.pointcontents (pos) & MASK_WATER) VectorCopy (pos, tr.endpos); else tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER); VectorAdd (water_start, tr.endpos, pos); VectorScale (pos, 0.5, pos); gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BUBBLETRAIL); gi.WritePosition (water_start); gi.WritePosition (tr.endpos); gi.multicast (pos, MULTICAST_PVS); } else if (sv_waterlevel->value) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BUBBLETRAIL); gi.WritePosition (start); gi.WritePosition (tr.endpos); gi.multicast (tr.endpos, MULTICAST_PHS); } else if (sv_tracers->value) { //add tracer for each bullet if not in water... VectorSubtract (tr.endpos, start, xEnd); VectorNormalize (xEnd); fire_tracer (self, start, xEnd, 2000); } } /* ================= fire_lead_2 bullets with pierce damage. Hits->loose25%damage->Hits-> ================= */ static void fire_lead_2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int mod) { vec3_t from; vec3_t end; trace_t tr; edict_t *ignore; int mask, counter=0; qboolean water; VectorMA (start, 8192, aimdir, end); VectorCopy (start, from); ignore = self; water = false; mask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA; if (!deathmatch->value&&!coop->value) { fire_bullet_slow (self, start, aimdir, sv_maxvelocity->value, kick, damage, mod, TE_BULLET_SPARKS); return; } while (ignore && countersvflags & SVF_MONSTER) || (tr.ent->client) || (tr.ent->solid == SOLID_BBOX)) ignore = tr.ent; else ignore = NULL; if ((tr.ent != self) && (tr.ent->takedamage)) { T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, mod); } else { if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BULLET_SPARKS); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.multicast (tr.endpos, MULTICAST_PVS); gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_GUNSHOT); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.multicast (tr.endpos, MULTICAST_PVS); } } } if (mod == MOD_RAILGUN) damage *= 0.9; else damage *= 0.5; VectorCopy (tr.endpos, from); } if (sv_waterlevel->value) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BUBBLETRAIL); gi.WritePosition (start); gi.WritePosition (tr.endpos); gi.multicast (tr.endpos, MULTICAST_PHS); } else if (sv_tracers->value) fire_tracer (self, start, aimdir, 2000); if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) BulletMark (self, &tr, mod); } /* ================= fire_bullet Fires a single round. Used for machinegun and chaingun. Would be fine for pistols, rifles, etc.... ================= */ void fire_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod) { fire_lead (self, start, aimdir, damage, kick, TE_GUNSHOT, hspread, vspread, mod); } //used for rail void fire_bullet_2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int mod) { fire_lead_2 (self, start, aimdir, damage, kick, mod); } //used for chaingun void fire_bullet_3 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod) { fire_lead (self, start, aimdir, damage, kick, TE_BLASTER, hspread, vspread, mod); } //used for blaster /* ================= fire_shotgun Shoots shotgun pellets. Used by shotgun and super shotgun. ================= */ void fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int mod) { int i; for (i = 0; i < count; i++) fire_lead (self, start, aimdir, damage, kick, TE_SHOTGUN, hspread, vspread, mod); } void fire_shotgun_2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int mod) { int i; for (i = 0; i < count; i++) fire_lead (self, start, aimdir, damage, kick, TE_SHOTGUN, hspread, vspread, mod); } void fire_shotgun_3 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int mod) { int i; for (i = 0; i < count; i++) fire_lead (self, start, aimdir, damage, kick, TE_BULLET_SPARKS, hspread, vspread, mod); } void touchNull (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) {} /* ================= fire_blaster Fires a single blaster bolt. Used by the blaster and hyper blaster. ================= */ void Flame_Burn (edict_t *bolt) { vec3_t up; //set upwards bloody fx up[0]=0;up[1]=0;up[2]=30;VectorNormalize (up); if (bolt->flamed!=bolt) VectorCopy (bolt->flamed->s.origin, bolt->s.origin); if (!Q_stricmp (bolt->classname, "tossedflame")) T_RadiusDamageFireLink(bolt); if (!bolt->flamed) bolt->flamed=bolt; if (bolt->flamed!=bolt) if (bolt->flamed) if (bolt->flamed->client) { if (bolt->flamed->client->pers.inventory[ITEM_INDEX(FindItem("Body Armor"))] || (bolt->flamed->client->stunts<-5 && (bolt->flamed->client->stunts+5)%10==0) ) { bolt->flamed->linked_flame=NULL; G_FreeEdict (bolt); return; } else T_Damage (bolt->flamed, bolt, bolt->owner, bolt->velocity, bolt->s.origin, up, bolt->dmg, 0, 0, bolt->mod); } T_RadiusDamageFire (bolt, bolt->owner, bolt->dmg*2, NULL, 100, MOD_FIRE); bolt->timer++; if (sv_serversideonly->value) { bolt->s.renderfx = 0; bolt->s.frame++; bolt->s.skinnum++; if (bolt->s.skinnum > 6) bolt->s.skinnum=2; if (bolt->s.frame > 7) { bolt->s.frame=4; } if (bolt->phase>4) { bolt->phase=0; gi.WriteByte (svc_temp_entity); gi.WriteByte (57); gi.WritePosition (bolt->s.origin); gi.multicast (bolt->s.origin, MULTICAST_PVS); } else bolt->phase++; } else { bolt->s.effects = EF_PLASMA; if (bolt->phase>6) { gi.positioned_sound (bolt->s.origin, bolt, CHAN_AUTO, gi.soundindex("world/explode_1.wav"), 1, ATTN_NORM, 0); bolt->phase=0; } else bolt->phase++; bolt->s.renderfx = 0; bolt->s.frame+=2; bolt->s.skinnum++; if (bolt->s.skinnum > 6) bolt->s.skinnum=4; bolt->s.frame=random() * 5; } bolt->nextthink = level.time; bolt->think = Flame_Burn; if (bolt->timer>(FIRELIFE*10)) { if (bolt->flamed) if (bolt->flamed->linked_flame) if (bolt->flamed->linked_flame==bolt) bolt->flamed->linked_flame=NULL; G_FreeEdict (bolt); return; } } void Linked_Flame (edict_t *flamed, edict_t *owner) { edict_t *bolt; trace_t tr; if (!(flamed->client || (flamed->svflags & SVF_MONSTER))) return; if (flamed->client) if (flamed->killer || flamed->client->aquasuit) return; if (flamed->linked_flame) G_FreeEdict (flamed->linked_flame); bolt = G_Spawn(); bolt->svflags = SVF_DEADMONSTER; bolt->flamed = flamed; VectorCopy (flamed->s.origin, bolt->s.origin); VectorCopy (flamed->s.old_origin, bolt->s.old_origin); //bolt->movetype = MOVETYPE_TOSS; bolt->movetype = MOVETYPE_NONE; bolt->timer = 0; bolt->s.renderfx = RF_TRANSLUCENT;; bolt->clipmask = MASK_SHOT; bolt->solid = SOLID_NOT; bolt->s.effects |= EF_HYPERBLASTER; VectorClear (bolt->mins); VectorClear (bolt->maxs); //bolt->s.modelindex = gi.modelindex ("sprites/s_flash.sp2"); bolt->s.modelindex = gi.modelindex ("models/objects/r_explode/tris.md2"); bolt->mod = MOD_FIRE; bolt->s.frame = 0; bolt->s.skinnum = 0; bolt->s.sound = gi.soundindex ("weapons/rockfly.wav"); bolt->owner = owner; bolt->nextthink = level.time; bolt->think = Flame_Burn; bolt->dmg = FLAMEDAMAGE; bolt->classname = "linkedflame"; gi.linkentity (bolt); if (!owner) bolt->owner=bolt; flamed->linked_flame=bolt; } void Tossed_Flame (vec3_t start) { edict_t *bolt; trace_t tr; bolt = G_Spawn(); bolt->svflags = SVF_DEADMONSTER; bolt->flamed = bolt; VectorCopy (start, bolt->s.origin); VectorCopy (start, bolt->s.old_origin); bolt->s.origin[2]+=25; bolt->movetype = MOVETYPE_BOUNCE; bolt->movetype = MOVETYPE_NONE; bolt->timer = 0; bolt->s.renderfx = RF_TRANSLUCENT; bolt->clipmask = MASK_SHOT; bolt->solid = SOLID_BBOX; //SOLID_NOT; bolt->s.effects |= EF_HYPERBLASTER; VectorClear (bolt->mins); VectorClear (bolt->maxs); //bolt->s.modelindex = gi.modelindex ("sprites/s_flash.sp2"); bolt->s.modelindex = gi.modelindex ("models/objects/r_explode/tris.md2"); bolt->mod = MOD_FLAME; bolt->s.frame = 0; bolt->s.skinnum = 0; bolt->s.sound = gi.soundindex ("weapons/rockfly.wav"); bolt->owner = bolt; bolt->nextthink = level.time; bolt->think = Flame_Burn; // VectorSet (bolt->mins, -100, -100, -100); // VectorSet (bolt->maxs, 100, 150, 100); bolt->dmg = FLAMEDAMAGE; bolt->classname = "tossedflame"; gi.linkentity (bolt); } void FlareExpire (edict_t *self) { int i=0; int color = (sv_waterlevel->value||self->waterlevel>1) ? SPLASH_UNKNOWN: SPLASH_LAVA; edict_t *findMark=NULL; gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_SPLASH); gi.WriteByte (8); gi.WritePosition (self->s.origin); gi.WriteDir (self->end_pt); gi.WriteByte (color); gi.multicast (self->s.origin, MULTICAST_PVS); self->timer++; self->nextthink = level.time; self->think = FlareExpire; if (self->timer == FLARETIME*10) BulletMarkThink(self); } void Blaster_Linked (edict_t *ent) { ent->timer++; ent->touch=touchNull; ent->s.sound = 0; //gi.soundindex ("misc/lasfly.wav"); if (ent->linkedto) { ent->solid=SOLID_NOT; if (ent->linkedto->health>=0) { ent->s.origin[0]=ent->linkedto->s.origin[0]+ent->link_offset[0]; ent->s.origin[1]=ent->linkedto->s.origin[1]+ent->link_offset[1]; ent->s.origin[2]=ent->linkedto->s.origin[2]+ent->link_offset[2]; } else if (ent->linkedtoclient) { ent->movetype = MOVETYPE_TOSS; ent->linkedto=NULL; ent->velocity[0]=0; ent->velocity[1]=0; ent->velocity[2]=0; } else { ent->movetype = MOVETYPE_TOSS; ent->linkedto=NULL; ent->velocity[0]=0; ent->velocity[1]=0; ent->velocity[2]=0; } } ent->think = Blaster_Linked; ent->nextthink = level.time; if (ent->timer>=100) G_FreeEdict(ent); } void blaster_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { int mod; if (other == self->owner) return; if (surf && (surf->flags & SURF_SKY)) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_NUKEBLAST); gi.WritePosition (self->s.origin); gi.multicast (self->s.origin, MULTICAST_PHS); G_FreeEdict (self); return; } if (self->owner->client) PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); if (other->takedamage) { mod = MOD_BLASTER2; T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod); if (sv_serversideonly->value) { VectorSubtract (self->s.origin, other->s.origin, self->link_offset); self->solid = SOLID_NOT; self->touch=NULL; self->linkedto = other; self->movetype = MOVETYPE_NONE; self->think = Blaster_Linked; self->nextthink = level.time; self->timer = (self->nextthink-level.time)*10; self->linkedtoclient=(other->client)?1:0; if (other->client) { if (other->health) { other->linked_ents[other->linked_ents_num]=self; other->linked_ents_num++; } else { self->linkedto = self; } } } } else { int color = (self->s.skinnum==4)? 0x40414243:(self->s.skinnum==3)? 0xdcdddedf: (self->s.skinnum==2)? 0xf2f2f0f0:(self->s.skinnum==1)? 0xdad0dcd2: 0xf3f3f1f1; int effect = (sv_waterlevel->value||self->waterlevel>1) ? TE_ELECTRIC_SPARKS:TE_BLASTER; if (sv_serversideonly->value) { gi.WriteByte (svc_temp_entity); gi.WriteByte (effect); gi.WritePosition (self->s.origin); if (!plane) { gi.WriteDir (vec3_origin); VectorCopy (vec3_origin, self->end_pt); } else { gi.WriteDir (plane->normal); VectorCopy (plane->normal, self->end_pt); } gi.multicast (self->s.origin, MULTICAST_PVS); self->movetype = MOVETYPE_NONE; self->timer = 0; self->nextthink = level.time; self->think = FlareExpire; self->s.sound = 0; } else { gi.sound(self, CHAN_AUTO, gi.soundindex("weapons/lashit.wav"), 0.75, ATTN_IDLE, 0); //coloured splash gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_LASER_SPARKS); gi.WriteByte (10); gi.WritePosition (self->s.origin); if (!plane) gi.WriteDir (vec3_origin); else gi.WriteDir (plane->normal); gi.WriteByte (color); gi.multicast (self->s.origin, MULTICAST_PVS); } } if (!sv_serversideonly->value) { self->think = G_FreeEdict; self->clipmask = 0; self->solid = SOLID_NOT; self->nextthink = level.time; } } void tracer_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { if (other == self->owner) return; if (surf && (surf->flags & SURF_SKY) && (self->s.frame == 0)) { G_FreeEdict (self); return; } if (self->waterlevel && (self->s.frame == 0)) { G_FreeEdict (self); return; } G_FreeEdict (self); return; } void blaster_touch_plasma (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { int mod, i; if ((other == self->owner)&&(self->s.frame<6)) return; if (surf && (surf->flags & SURF_SKY) && (self->s.frame == 0)) { G_FreeEdict (self); return; } if (surf && (surf->flags & SURF_WARP)) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_HEATBEAM_SPARKS); gi.WritePosition (self->s.origin); if (!plane) { gi.WriteDir (vec3_origin); VectorCopy (vec3_origin, self->end_pt); } else { gi.WriteDir (plane->normal); VectorCopy (plane->normal, self->end_pt); } gi.multicast (self->s.origin, MULTICAST_PVS); G_FreeEdict (self); return; } if (self->waterlevel && (self->s.frame == 0)) { G_FreeEdict (self); return; } if (self->owner->client) PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); if (other->takedamage) { mod = MOD_HYPERBLASTER; if (other->client) { if (!other->client->pers.inventory[ITEM_INDEX(FindItem("Body Armor"))]) { Linked_Flame (other, self->owner); T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod); } } else { Linked_Flame (other, self->owner); T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod); } gi.sound(self, CHAN_AUTO, gi.soundindex("boss3/d_hit.wav"), 0.5, ATTN_IDLE, 0); } else { if (self->flaming) if (random()<0.2) Tossed_Flame (self->s.origin); } if (!self->flaming) { self->movetype = MOVETYPE_NONE; if (self->s.frame==0) self->s.frame = 1; self->timer = 0; } } qboolean Check_RadiusFire (vec3_t start); void blaster_touch_hyper (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { int mod, i; if (other == self->owner) return; if (surf && (surf->flags & SURF_SKY)) { G_FreeEdict (self); return; } if (surf && (surf->flags & SURF_WARP)) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_HEATBEAM_SPARKS); gi.WritePosition (self->s.origin); if (!plane) { gi.WriteDir (vec3_origin); VectorCopy (vec3_origin, self->end_pt); } else { gi.WriteDir (plane->normal); VectorCopy (plane->normal, self->end_pt); } gi.multicast (self->s.origin, MULTICAST_PVS); G_FreeEdict (self); return; } if (other->takedamage) { mod=MOD_FIRE; if (other->client) { if (!other->client->pers.inventory[ITEM_INDEX(FindItem("Body Armor"))]) { Linked_Flame (other, self->owner); T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod); } } else { Linked_Flame (other, self->owner); T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod); } if (self->timer>3) G_FreeEdict (self); return; } else if (Check_RadiusFire(self->s.origin)) { Tossed_Flame (self->s.origin); G_FreeEdict (self); return; } } void plasma_think (edict_t *bolt) { if (bolt->waterlevel) { G_FreeEdict (bolt); return; } bolt->timer++; if (bolt->s.frame == 0) { if (bolt->timer == 3) { bolt->s.effects = EF_SPHERETRANS; //bolt->movetype = MOVETYPE_NONE; bolt->timer =0; bolt->s.frame++; bolt->dmg *= 0.5; } } else if (bolt->s.frame == 1) { if (bolt->timer == 1) { bolt->s.effects = EF_SPHERETRANS; bolt->timer =0; bolt->s.frame++; bolt->dmg *= 0.5; } } bolt->nextthink = level.time;// + FRAMETIME; if ((bolt->s.frame==2)&&(bolt->timer==1)) bolt->think = G_FreeEdict; } void hyper_think (edict_t *bolt) { vec3_t dir; if (bolt->waterlevel) { G_FreeEdict (bolt); return; } bolt->timer++; if (bolt->timer==(2*((int)(bolt->timer*0.5))) ) bolt->s.skinnum++; bolt->s.frame+=2; bolt->dmg = bolt->dmg - (bolt->dmg * 0.1); bolt->nextthink = level.time + FRAMETIME; if (bolt->timer == 1) bolt->s.renderfx = RF_TRANSLUCENT; if (bolt->s.skinnum==6) bolt->s.skinnum=5; if (bolt->timer == 6) { G_FreeEdict(bolt); return; } if (VectorLength(bolt->velocity)) { VectorScale (bolt->velocity, 2, bolt->velocity); VectorCopy (bolt->velocity, dir); VectorNormalize (dir); dir[2]-=90; vectoangles (dir, bolt->s.angles); if (VectorLength(bolt->velocity)>FLAMESPEEDLIMIT); { VectorNormalize (bolt->velocity); VectorScale (bolt->velocity, FLAMESPEEDLIMIT, bolt->velocity); } } } void fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper) { edict_t *bolt; trace_t tr; int i; if (GameSlowMo) speed *= SLOWMOMULT; VectorNormalize (dir); bolt = G_Spawn(); bolt->svflags = SVF_DEADMONSTER; // yes, I know it looks weird that projectiles are deadmonsters // what this means is that when prediction is used against the object // (blaster/hyperblaster shots), the player won't be solid clipped against // the object. Right now trying to run into a firing hyperblaster // is very jerky since you are predicted 'against' the shots. VectorCopy (start, bolt->s.origin); VectorCopy (start, bolt->s.old_origin); vectoangles (dir, bolt->s.angles); VectorScale (dir, speed, bolt->velocity); bolt->movetype = MOVETYPE_FLYMISSILE; bolt->clipmask = MASK_SHOT; bolt->solid = SOLID_BBOX; VectorClear (bolt->mins); VectorClear (bolt->maxs); if (!sv_serversideonly->value) { bolt->s.modelindex = gi.modelindex ("models/objects/swq_laser/tris.md2"); bolt->s.effects = EF_PLASMA; bolt->s.renderfx = RF_FULLBRIGHT; bolt->s.skinnum = effect; } else { bolt->s.modelindex = gi.modelindex ("models/objects/laser/tris.md2"); if (!sv_waterlevel->value) bolt->s.effects |= effect; else { bolt->s.effects |= EF_GRENADE | EF_PLASMA; } } bolt->s.sound = gi.soundindex ("misc/lasfly.wav"); bolt->owner = self; bolt->touch = blaster_touch; bolt->nextthink = level.time + 10; bolt->think = G_FreeEdict; bolt->timer=0; bolt->dmg = damage; bolt->linkedto = NULL; bolt->classname = "bolt"; if (hyper) bolt->spawnflags = 1; gi.linkentity (bolt); if (self->client) check_dodge (self, bolt->s.origin, dir, speed); if (!sv_serversideonly->value) { tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, bolt->clipmask); if (tr.fraction != 1) { tr = gi.trace (tr.endpos, NULL, NULL, start, self, bolt->clipmask); VectorCopy(tr.endpos, bolt->s.origin); bolt->touch (bolt, tr.ent, &tr.plane, tr.surface); } } else { tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT); if (tr.fraction < 1.0) { VectorMA (bolt->s.origin, -10, dir, bolt->s.origin); bolt->touch (bolt, tr.ent, NULL, NULL); } } } void TracerDie (edict_t *self) { int i=0; edict_t *findMark=NULL; if (!self) return; while (findMark!=self) { findMark=tracerptr[i]; i++; if (i>tracers) break; } for (i=i-1; i < tracers;i++) tracerptr[i] = tracerptr[i+1]; tracerptr[tracers]=NULL; tracers--; G_FreeEdict(self); } void fire_tracer (edict_t *self, vec3_t start, vec3_t dir, int speed) { edict_t *bolt; trace_t tr; int i; if (sv_tracers->value <= tracers) TracerDie (tracerptr[0]); VectorNormalize (dir); bolt = G_Spawn(); bolt->svflags = SVF_DEADMONSTER; VectorCopy (start, bolt->s.origin); VectorCopy (start, bolt->s.old_origin); vectoangles (dir, bolt->s.angles); VectorScale (dir, speed, bolt->velocity); bolt->movetype = MOVETYPE_FLYMISSILE; bolt->clipmask = MASK_SHOT; bolt->solid = SOLID_BBOX; VectorClear (bolt->mins); VectorClear (bolt->maxs); bolt->owner = self; bolt->touch = tracer_touch; bolt->nextthink = level.time + 10; bolt->think = TracerDie; bolt->dmg = 0; bolt->classname = "bolt"; //bolt->s.renderfx |= RF_GLOW; bolt->s.renderfx |= RF_TRANSLUCENT; if (sv_serversideonly->value) bolt->s.modelindex = gi.modelindex ("models/objects/laser/tris.md2"); else bolt->s.modelindex = gi.modelindex ("models/objects/tracr/tris.md2"); //bolt->s.modelindex = gi.modelindex ("models/monsters/parasite/segment/tris.md2"); bolt->spawnflags = 1; gi.linkentity (bolt); if (self->client) check_dodge (self, bolt->s.origin, dir, speed); tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT); if (tr.fraction < 1.0) { VectorMA (bolt->s.origin, -10, dir, bolt->s.origin); bolt->touch (bolt, tr.ent, NULL, NULL); } tracerptr[tracers] = bolt; tracers++; } void fire_blaster_hyper (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper) { edict_t *bolt; trace_t tr; VectorNormalize (dir); bolt = G_Spawn(); bolt->svflags = SVF_DEADMONSTER; VectorCopy (start, bolt->s.origin); VectorCopy (start, bolt->s.old_origin); VectorCopy (dir, bolt->end_pt); vectoangles (dir, bolt->s.angles); VectorScale (dir, speed, bolt->velocity); bolt->movetype = MOVETYPE_FLYMISSILE; //bolt->movetype = MOVETYPE_TOSS; bolt->timer = 0; bolt->think = hyper_think; bolt->clipmask = MASK_SHOT|MASK_WATER; bolt->solid = SOLID_BBOX; bolt->s.effects |= effect; VectorClear (bolt->mins); VectorClear (bolt->maxs); bolt->s.modelindex = gi.modelindex ("models/objects/r_explode/tris.md2"); bolt->s.skinnum = 3; bolt->s.frame = 0; bolt->s.renderfx = RF_TRANSLUCENT; vectoangles (dir, bolt->s.angles); bolt->classname = "flame"; bolt->s.renderfx |= RF_IR_VISIBLE; bolt->s.sound = gi.soundindex ("weapons/rockfly.wav"); bolt->owner = self; bolt->touch = blaster_touch_hyper; bolt->nextthink = level.time + FRAMETIME; bolt->dmg = damage; bolt->flaming = 1; if (hyper) bolt->spawnflags = 1; gi.linkentity (bolt); if (self->client) check_dodge (self, bolt->s.origin, dir, speed); tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT); if (tr.fraction < 1.0) { VectorMA (bolt->s.origin, -10, dir, bolt->s.origin); bolt->touch (bolt, tr.ent, NULL, NULL); } } void fire_plasma (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper) { edict_t *bolt; trace_t tr; VectorNormalize (dir); bolt = G_Spawn(); bolt->svflags = SVF_DEADMONSTER; VectorCopy (start, bolt->s.origin); VectorCopy (start, bolt->s.old_origin); vectoangles (dir, bolt->s.angles); VectorScale (dir, speed, bolt->velocity); //bolt->movetype = MOVETYPE_FLYMISSILE; bolt->movetype = MOVETYPE_TOSS; bolt->timer = 0; bolt->s.effects=EF_PLASMA; //bolt->s.renderfx |= RF_TRANSLUCENT; bolt->clipmask = MASK_SHOT|MASK_WATER; bolt->solid = SOLID_BBOX; bolt->s.effects |= effect; VectorClear (bolt->mins); VectorClear (bolt->maxs); if (sv_serversideonly->value) bolt->s.modelindex = gi.modelindex ("sprites/s_flash.sp2"); else bolt->s.modelindex = gi.modelindex ("sprites/s_flash2.sp2"); bolt->s.frame = 0; bolt->flamed=0; //pick the sprite... //bolt->s.sound = gi.soundindex ("weapons/rockfly.wav"); bolt->owner = self; bolt->touch = blaster_touch_plasma; //bolt->nextthink = level.time + 2; bolt->think = plasma_think; bolt->nextthink = level.time;// + FRAMETIME; //bolt->think = G_FreeEdict; bolt->dmg = damage; bolt->flaming = 0; bolt->classname = "flame"; if (hyper) bolt->spawnflags = 1; gi.linkentity (bolt); if (self->client) check_dodge (self, bolt->s.origin, dir, speed); tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT); if (tr.fraction < 1.0) { VectorMA (bolt->s.origin, -10, dir, bolt->s.origin); bolt->touch (bolt, tr.ent, NULL, NULL); } } /* ================= fire_grenade ================= */ static void C4_Explode (edict_t *ent) { vec3_t origin; int mod; if (ent->owner->client) PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); //FIXME: if we are onground then raise our Z just a bit since we are a point? if (ent->enemy) { float points; vec3_t v; vec3_t dir; VectorAdd (ent->enemy->mins, ent->enemy->maxs, v); VectorMA (ent->enemy->s.origin, 0.5, v, v); VectorSubtract (ent->s.origin, v, v); points = ent->dmg - 0.5 * VectorLength (v); VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir); mod = MOD_C4; T_Damage (ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); } mod = MOD_C4; T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod); if (sv_serversideonly->value) T_FlashRadius(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius); target_earthquake_think (ent); //SP_target_earthquake (origin); bigExplosion(ent->s.origin, vec3_origin, 2); if (sv_serversideonly->value) ExplodeMark (ent, ent->s.origin, level.time + 5 + random()*2 ); G_FreeEdict (ent); } static void Grenade_Explode (edict_t *ent) { vec3_t origin; int mod; if (ent->owner->client) PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); //FIXME: if we are onground then raise our Z just a bit since we are a point? if (ent->enemy) { float points; vec3_t v; vec3_t dir; VectorAdd (ent->enemy->mins, ent->enemy->maxs, v); VectorMA (ent->enemy->s.origin, 0.5, v, v); VectorSubtract (ent->s.origin, v, v); points = ent->dmg - 0.5 * VectorLength (v); VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir); if (ent->spawnflags & 1) mod = MOD_HANDGRENADE; else mod = MOD_GRENADE; T_Damage (ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); } if (ent->spawnflags & 2) mod = MOD_HELD_GRENADE; else if (ent->spawnflags & 1) mod = MOD_HG_SPLASH; else mod = MOD_G_SPLASH; T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod); VectorMA (ent->s.origin, -0.02, ent->velocity, origin); if (ent->waterlevel||sv_waterlevel->value) tempent (TE_GRENADE_EXPLOSION_WATER, origin, NULL); else tempent (TE_GRENADE_EXPLOSION, origin, NULL); G_FreeEdict (ent); } static void Grenade_Flash_Explode (edict_t *ent) { vec3_t origin; vec3_t up; up[0]=up[1]=0; up[2]=1; if (ent->owner->client) PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); T_FlashRadius(ent, ent->owner, ent->dmg*100, ent->enemy, ent->dmg_radius*100); VectorMA (ent->s.origin, -0.02, ent->velocity, origin); gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_GUNSHOT); gi.WritePosition (ent->s.origin); gi.WriteDir (up); gi.multicast (ent->s.origin, MULTICAST_PVS); gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_GUNSHOT); gi.WritePosition (ent->s.origin); gi.WriteDir (up); gi.multicast (ent->s.origin, MULTICAST_PVS); gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BLASTER); gi.WritePosition (ent->s.origin); gi.WriteDir (up); gi.multicast (ent->s.origin, MULTICAST_PVS); gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BLASTER); gi.WritePosition (ent->s.origin); gi.WriteDir (up); gi.multicast (ent->s.origin, MULTICAST_PVS); gi.sound (ent, CHAN_VOICE, gi.soundindex ("world/explod2.wav"), 1, ATTN_NORM, 0); G_FreeEdict (ent); } static void Grenade_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) { int damage=(abs(ent->velocity[0])+abs(ent->velocity[1])+abs(ent->velocity[2]))/30; if (other == ent->owner) return; if (surf && (surf->flags & SURF_SKY)) { G_FreeEdict (ent); return; } if (!other->takedamage) { if (ent->spawnflags & 1) { if (random() > 0.5) gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0); else gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0); } else { gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0); } return; } else if (damage) { T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, damage, 0, 0, MOD_GREN_HIT); gi.sound (ent, CHAN_VOICE, gi.soundindex ("chick/chkfall1.wav"), 0.5, ATTN_NORM, 0); } ent->enemy = other; Grenade_Explode (ent); } static void Grenade_Touch_2 (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) { int damage=(abs(ent->velocity[0])+abs(ent->velocity[1])+abs(ent->velocity[2]))/30; if (!other->takedamage) { if (!(surf && (surf->flags & SURF_SKY))) { if (ent->spawnflags & 1) { if (random() > 0.5) gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0); else gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0); } else { gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0); } } return; } else if (damage) { T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, damage, 0, 0, MOD_GREN_HIT); gi.sound (ent, CHAN_VOICE, gi.soundindex ("chick/chkfall1.wav"), 1, ATTN_NORM, 0); } /* ent->enemy = other; Grenade_Explode (ent);*/ } static void C4_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) { if (other == ent->owner) return; if (surf && (surf->flags & SURF_SKY)) { G_FreeEdict (ent); return; } if (!other->takedamage) { ent->movetype = MOVETYPE_NONE; vectoangles(plane->normal, ent->s.angles); } if (other->takedamage) { VectorSubtract (ent->s.origin, other->s.origin, ent->link_offset); ent->solid = SOLID_NOT; ent->touch=NULL; ent->linkedto = other; ent->movetype = MOVETYPE_NONE; if (other->client) { ent->linkedtoclient=1; other->linked_ents[other->linked_ents_num]=ent; other->linked_ents_num++; } ent->touch=touchNull; } } static void Sticky_Linked_Think (edict_t *ent) { ent->touch=touchNull; if (ent->linkedto) { if (ent->linkedto->health>=0) { ent->s.origin[0]=ent->linkedto->s.origin[0]+ent->link_offset[0]; ent->s.origin[1]=ent->linkedto->s.origin[1]+ent->link_offset[1]; ent->s.origin[2]=ent->linkedto->s.origin[2]+ent->link_offset[2]; } else if (ent->linkedtoclient) { ent->movetype = MOVETYPE_TOSS; ent->linkedto=NULL; ent->velocity[0]=0; ent->velocity[1]=0; ent->velocity[2]=0; } else { ent->movetype = MOVETYPE_TOSS; ent->linkedto=NULL; ent->velocity[0]=0; ent->velocity[1]=0; ent->velocity[2]=0; } } if (ent->timer>=20) ent->thinklinked(ent); ent->nextthink = level.time; ent->think = Sticky_Linked_Think; ent->timer++; } static void Sticky_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) { int damage=(abs(ent->velocity[0])+abs(ent->velocity[1])+abs(ent->velocity[2]))/30; if (surf && (surf->flags & SURF_SKY)) { // G_FreeEdict (ent); return; } if (!other->takedamage) { ent->movetype = MOVETYPE_NONE; gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0); } //impact hurts like hell!! if (other->takedamage&&damage) { ent->linkedto=other; ent->timer=0; ent->nextthink = level.time; ent->thinklinked=ent->think; ent->think=Sticky_Linked_Think; ent->touch=NULL; ent->movetype = MOVETYPE_NONE; ent->linkedtoclient=(other->client)?1:0; VectorSubtract (ent->s.origin, other->s.origin, ent->link_offset); Sticky_Linked_Think(ent); if (other->client) { other->linked_ents[other->linked_ents_num]=ent; other->linked_ents_num++; } T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, damage, 0, 0, MOD_GREN_HIT); gi.sound (ent, CHAN_VOICE, gi.soundindex ("chick/chkfall1.wav"), 1, ATTN_NORM, 0); } else { ent->linkedto=NULL; } } static void C4_Think (edict_t *ent) { ent->timer++; if (ent->linkedto) { ent->solid=SOLID_NOT; if (ent->linkedto->health>0) { ent->s.origin[0]=ent->linkedto->s.origin[0]+ent->link_offset[0]; ent->s.origin[1]=ent->linkedto->s.origin[1]+ent->link_offset[1]; ent->s.origin[2]=ent->linkedto->s.origin[2]+ent->link_offset[2]; } else if (ent->linkedtoclient) { ent->movetype = MOVETYPE_TOSS; ent->linkedto=NULL; ent->velocity[0]=0; ent->velocity[1]=0; ent->velocity[2]=0; } else { ent->movetype = MOVETYPE_TOSS; ent->linkedto=NULL; ent->velocity[0]=0; ent->velocity[1]=0; ent->velocity[2]=0; } } ent->think = C4_Think; ent->nextthink = level.time; if (ent->timer>=75) C4_Explode(ent); } static void Grenade_Cluster (edict_t *ent) { vec3_t origin, aimdir; int mod, i, x; int damage = 75; int damageR= 125; int speed = 175; int speed2 = 135; if (ent->owner->client) PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); //FIXME: if we are onground then raise our Z just a bit since we are a point? if (ent->enemy) { float points; vec3_t v; vec3_t dir; VectorAdd (ent->enemy->mins, ent->enemy->maxs, v); VectorMA (ent->enemy->s.origin, 0.5, v, v); VectorSubtract (ent->s.origin, v, v); points = ent->dmg - 0.5 * VectorLength (v); VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir); if (ent->spawnflags & 1) mod = MOD_HANDGRENADE; else mod = MOD_GRENADE; T_Damage (ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); } if (ent->spawnflags & 2) mod = MOD_HELD_GRENADE; else if (ent->spawnflags & 1) mod = MOD_HG_SPLASH; else mod = MOD_G_SPLASH; T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod); VectorMA (ent->s.origin, -0.02, ent->velocity, origin); if (ent->waterlevel||sv_waterlevel->value) tempent (TE_GRENADE_EXPLOSION_WATER, origin, NULL); else tempent (TE_GRENADE_EXPLOSION, origin, NULL); aimdir[2]= 1; aimdir[0]= -1; aimdir[1]= 0; fire_grenade3 (ent->owner, ent->s.origin, aimdir, damage, speed, 1.1, damageR); aimdir[0]= 0; aimdir[1]= -1; fire_grenade3 (ent->owner, ent->s.origin, aimdir, damage, speed, 1.2, damageR); aimdir[0]= 1; aimdir[1]= 0; fire_grenade3 (ent->owner, ent->s.origin, aimdir, damage, speed, 1.3, damageR); aimdir[0]= 0; aimdir[1]= 1; fire_grenade3 (ent->owner, ent->s.origin, aimdir, damage, speed, 1.4, damageR); aimdir[0]= -1; aimdir[1]= 1; fire_grenade3 (ent->owner, ent->s.origin, aimdir, damage, speed2, 1.6, damageR); aimdir[0]= -1; aimdir[1]= -1; fire_grenade3 (ent->owner, ent->s.origin, aimdir, damage, speed2, 1.7, damageR); aimdir[0]= 1; aimdir[1]= -1; fire_grenade3 (ent->owner, ent->s.origin, aimdir, damage, speed2, 1.8, damageR); aimdir[0]= 1; aimdir[1]= 1; fire_grenade3 (ent->owner, ent->s.origin, aimdir, damage, speed2, 1.9, damageR); G_FreeEdict (ent); } void steam_think (edict_t *ent) { trace_t tr; vec3_t end; ent->nextthink = level.time; VectorCopy(ent->s.origin, ent->s.old_origin); AngleVectors (ent->s.angles, end, NULL, NULL); VectorMA(ent->s.origin, 4, end, end); end[2] += 7; tr = gi.trace(ent->s.origin, NULL, NULL, end, ent, CONTENTS_SOLID); VectorCopy(tr.endpos, ent->s.origin); ent->speed += ent->accel; if (ent->speed < 0) ent->speed = 0; if (ent->count++ == ent->wait) { ent->count = 0; ent->s.frame++; } if (ent->s.frame==4) G_FreeEdict(ent); } static void Grenade_Gas (edict_t *ent) { int gastime; vec3_t origin; vec3_t up; up[0]=0; up[1]=0; up[2]=2; ent->timer++; if (ent->linkedto) { ent->solid=SOLID_NOT; if (ent->linkedto->health>=0) { ent->s.origin[0]=ent->linkedto->s.origin[0]+ent->link_offset[0]; ent->s.origin[1]=ent->linkedto->s.origin[1]+ent->link_offset[1]; ent->s.origin[2]=ent->linkedto->s.origin[2]+ent->link_offset[2]; } else if (ent->linkedtoclient) { ent->movetype = MOVETYPE_TOSS; ent->linkedto=NULL; ent->velocity[0]=0; ent->velocity[1]=0; ent->velocity[2]=0; } else { ent->movetype = MOVETYPE_TOSS; ent->linkedto=NULL; ent->velocity[0]=0; ent->velocity[1]=0; ent->velocity[2]=0; } } //if (((int)(ent->timer/10))*10 == ent->timer) // spawnGas(ent->s.origin); ent->s.sound = gi.soundindex("world/steam2.wav"); T_RadiusDamageGas(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, MOD_GASGREN); if (!coop->value && !deathmatch->value) gastime = 2; else gastime = 4; if (sv_serversideonly->value) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_STEAM); gi.WriteShort ((short int)-1); gi.WriteByte (10); //Amount gi.WritePosition (ent->s.origin); //position gi.WriteDir (up); //Direction gi.WriteByte (208); //color gi.WriteShort (200); //speed gi.multicast (ent->s.origin, MULTICAST_PVS); /* 6-9 - varying whites (darker to brighter) 224 - sparks 176 - blue water 80 - brown water 208 - slime 232 - blood */ } else if ((int)(level.time*10)%gastime==0) { edict_t *steam; steam = G_Spawn(); steam->wait = 2; steam->count = 0; steam->solid = SOLID_NOT; steam->movetype = MOVETYPE_NONE; VectorCopy(ent->s.origin, steam->s.origin); VectorSet(steam->s.angles, 0, ent->style+=30, 0); steam->think = steam_think; steam->nextthink = level.time; steam->s.modelindex = gi.modelindex ("sprites/s_smoke.sp2"); steam->s.renderfx = RF_TRANSLUCENT; steam->s.frame = 0; gi.linkentity (steam); } ent->think = Grenade_Gas; ent->nextthink = level.time; if (ent->timer>=100) FadeDieSink(ent); } void fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius) { edict_t *grenade; vec3_t dir; vec3_t forward, right, up; vectoangles (aimdir, dir); AngleVectors (dir, forward, right, up); grenade = G_Spawn(); VectorCopy (start, grenade->s.origin); VectorScale (aimdir, speed, grenade->velocity); VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity); VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity); VectorAdd(grenade->velocity, self->velocity, grenade->velocity); VectorSet (grenade->avelocity, 300, 300, 300); grenade->movetype = MOVETYPE_BOUNCE; grenade->clipmask = MASK_SHOT; grenade->solid = SOLID_BBOX; grenade->s.effects |= EF_GRENADE; VectorClear (grenade->mins); VectorClear (grenade->maxs); grenade->s.modelindex = gi.modelindex ("models/objects/grenade/tris.md2"); grenade->owner = self; grenade->touch = Grenade_Touch; grenade->nextthink = level.time + timer; grenade->think = Grenade_Explode; grenade->dmg = damage; grenade->dmg_radius = damage_radius; grenade->classname = "grenade"; gi.linkentity (grenade); } void fire_grenade3 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius) { edict_t *grenade; vec3_t dir; vec3_t forward, right, up; vectoangles (aimdir, dir); AngleVectors (dir, forward, right, up); grenade = G_Spawn(); VectorCopy (start, grenade->s.origin); VectorScale (aimdir, speed, grenade->velocity); VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity); VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity); VectorAdd(grenade->velocity, self->velocity, grenade->velocity); VectorSet (grenade->avelocity, 300, 300, 300); grenade->movetype = MOVETYPE_BOUNCE; grenade->clipmask = MASK_SHOT; grenade->solid = SOLID_BBOX; grenade->s.effects |= EF_GRENADE; VectorClear (grenade->mins); VectorClear (grenade->maxs); grenade->s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2"); grenade->owner = self; grenade->touch = Grenade_Touch; grenade->nextthink = level.time + timer; grenade->think = Grenade_Explode; grenade->dmg = damage; grenade->dmg_radius = damage_radius; grenade->classname = "grenade"; gi.linkentity (grenade); } void fire_grenade2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held) { edict_t *grenade; vec3_t dir; vec3_t forward, right, up; vectoangles (aimdir, dir); AngleVectors (dir, forward, right, up); grenade = G_Spawn(); VectorCopy (start, grenade->s.origin); VectorScale (aimdir, speed, grenade->velocity); VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity); VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity); VectorSet (grenade->avelocity, 300, 300, 300); grenade->movetype = MOVETYPE_BOUNCE; grenade->clipmask = MASK_SHOT; grenade->solid = SOLID_BBOX; grenade->s.effects |= EF_GRENADE; VectorClear (grenade->mins); VectorClear (grenade->maxs); //grenade->s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2"); grenade->s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2"); //weap fire mode grenade->dmg = damage; grenade->dmg_radius = damage_radius; grenade->owner = self; grenade->touch = Grenade_Touch_2; grenade->nextthink = level.time + timer; grenade->think = Grenade_Explode; //weap fire mode if (held) { VectorAdd(grenade->velocity, self->velocity, grenade->velocity); if (self->client) { if (self->client->grnl_set == 1) grenade->touch = Sticky_Touch; if (self->client->gren_set == 2) { grenade->dmg_radius *= 0.25; grenade->dmg = 3; grenade->think = Grenade_Gas; grenade->style = 0; } else if (self->client->gren_set == 1) grenade->think = Grenade_Flash_Explode; else if (self->client->gren_set == 3) { grenade->think = Grenade_Cluster; grenade->dmg_radius *= 0.25; grenade->dmg *= 0.25; } } } else { if (self->client->gren_set==1) { grenade->think = Grenade_Flash_Explode; } else if (self->client->gren_set==2) { grenade->dmg_radius *= 0.25; grenade->dmg = 1; grenade->think = Grenade_Gas; grenade->style = 0; } else if (self->client->gren_set==3) { grenade->dmg_radius *= 0.25; grenade->dmg *= 0.25; grenade->think = Grenade_Cluster; } } grenade->timer=0; grenade->classname = "hgrenade"; if (held) grenade->spawnflags = 3; else grenade->spawnflags = 1; grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav"); if (timer <= 0.0) Grenade_Explode (grenade); else { gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/hgrent1a.wav"), 1, ATTN_NORM, 0); gi.linkentity (grenade); } } void fire_c4 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held) { edict_t *grenade; vec3_t dir; vec3_t forward, right, up; vectoangles (aimdir, dir); AngleVectors (dir, forward, right, up); grenade = G_Spawn(); VectorCopy (start, grenade->s.origin); VectorScale (aimdir, speed, grenade->velocity); VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity); VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity); VectorSet (grenade->avelocity, 300, 300, 300); grenade->movetype = MOVETYPE_TOSS; grenade->clipmask = MASK_SHOT; grenade->solid = SOLID_BBOX; grenade->linkedto = NULL; VectorClear (grenade->mins); VectorClear (grenade->maxs); /* grenade->s.modelindex = gi.modelindex ("models/objects/grenade/tris.md2"); grenade->s.renderfx = RF_SHELL_HALF_DAM; //*/ //* grenade->s.effects = EF_TELEPORTER; grenade->s.renderfx = RF_SHELL_DOUBLE; grenade->s.modelindex = gi.modelindex ("models/objects/minelite/light1/tris.md2"); //*/ grenade->owner = self; grenade->touch = C4_Touch; grenade->nextthink = level.time;// + timer; grenade->think = C4_Think; // C4_Explode; grenade->dmg = damage; grenade->dmg_radius = damage_radius; grenade->classname = "hgrenade"; grenade->spawnflags = 1; grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav"); gi.linkentity (grenade); } /* ================= fire_rocket ================= */ void Rocket_Tilt (edict_t *ent) { vec3_t dir, old_vel; int limit=100; int speed = VectorLength(ent->velocity); int HOMING_IMPACT = (ent->waterlevel)? 90 : 50; if (ent->owner->client) if ((ent->owner->client->weaphold==11)&&(ent->owner->client->laser_on)) { limit=6000; VectorCopy (ent->velocity, old_vel); VectorNormalize (old_vel); VectorSubtract (ent->owner->client->laser_ptr, ent->s.origin, dir); VectorNormalize (dir); if (!ent->owner->client->laser_ptr) { VectorCopy (old_vel, dir); } VectorScale(old_vel, 100 - HOMING_IMPACT ,old_vel); VectorScale(dir, HOMING_IMPACT ,dir); VectorAdd(dir, old_vel, ent->velocity); VectorNormalize (ent->velocity); VectorScale(ent->velocity, speed ,ent->velocity); } if (ent->linkedto) { limit=100; VectorSubtract (ent->linkedto->s.origin, ent->s.origin, dir); VectorNormalize (dir); VectorScale(dir,VectorLength(ent->velocity),ent->velocity); } VectorCopy (ent->velocity, dir); VectorNormalize (dir); vectoangles (dir, ent->s.angles); ent->timer++; if (ent->timer>limit) { G_FreeEdict (ent); return; } ent->nextthink = level.time; } void Rocket_Camera (edict_t *ent) { int i; int speed = 500; vec3_t dir; AngleVectors (ent->owner->client->v_angle, dir, NULL, NULL); VectorNormalize(dir); for (i=0;i<3;i++) { ent->velocity[i]=0; ent->velocity[i]+=dir[i]*speed; } VectorNormalize (dir); vectoangles (dir, ent->s.angles); //send player view location to missle... ent->think = Rocket_Camera; ent->nextthink = level.time; } void rocket_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) { vec3_t origin; int n; if (other == ent->owner) return; if (surf && (surf->flags & SURF_SKY)) { if (ent->owner->client) { if (ent->movetype == MOVETYPE_FLYMISSILE) G_FreeEdict (ent); } else G_FreeEdict (ent); return; } if (ent->owner->client) PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); // calculate position for the explosion entity VectorMA (ent->s.origin, -0.02, ent->velocity, origin); if (other->takedamage) { T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET); } else { // don't throw any debris in net games /* if (!deathmatch->value && !coop->value) { if ((surf) && !(surf->flags & (SURF_WARP|SURF_TRANS33|SURF_TRANS66|SURF_FLOWING))) { n = rand() % 5; while(n--) ThrowDebris (ent, "models/objects/debris2/tris.md2", 2, ent->s.origin); } } */ } T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH); if (ent->waterlevel||sv_waterlevel->value) tempent (TE_ROCKET_EXPLOSION_WATER, origin, NULL); else tempent (TE_ROCKET_EXPLOSION, origin, NULL); G_FreeEdict (ent); } void rocket_bfg_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) { vec3_t origin; int n; if (other == ent->owner) return; if (surf && (surf->flags & SURF_SKY)) { //G_FreeEdict (ent); return; } if (ent->owner->client) PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); // calculate position for the explosion entity VectorMA (ent->s.origin, -0.02, ent->velocity, origin); if (other->takedamage) { T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET_BFG); } else { // don't throw any debris in net games /* if (!deathmatch->value && !coop->value) { if ((surf) && !(surf->flags & (SURF_WARP|SURF_TRANS33|SURF_TRANS66|SURF_FLOWING))) { n = rand() % 5; while(n--) ThrowDebris (ent, "models/objects/debris2/tris.md2", 2, ent->s.origin); } } */ } T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_BFG_SPLASH); if (sv_serversideonly->value) T_FlashRadius(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius*1.5); if (plane) bigExplosion(ent->s.origin, plane->normal, 1); else bigExplosion(ent->s.origin, vec3_origin, 1); if (sv_serversideonly->value) ExplodeMark (ent, ent->s.origin, level.time + 5 + random()*2 ); target_earthquake_think (ent); G_FreeEdict (ent); } // CCH: Explode rocket without touching anything static void Rocket_Explode (edict_t *ent) { vec3_t origin; if (ent->owner->client) PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); // calculate position for the explosion entity VectorMA (ent->s.origin, -0.02, ent->velocity, origin); T_RadiusDamage(ent, ent->owner, ent->radius_dmg, NULL, ent->dmg_radius, MOD_R_SPLASH); if (ent->waterlevel||sv_waterlevel->value) tempent (TE_ROCKET_EXPLOSION_WATER, origin, NULL); else tempent (TE_ROCKET_EXPLOSION, origin, NULL); G_FreeEdict (ent); } // CCH: When a rocket 'dies', it blows up next frame static void Rocket_Die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) { self->takedamage = DAMAGE_NO; self->nextthink = level.time + .1; self->think = Rocket_Explode; } void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage) { edict_t *rocket; rocket = G_Spawn(); VectorCopy (start, rocket->s.origin); VectorCopy (dir, rocket->movedir); vectoangles (dir, rocket->s.angles); VectorScale (dir, speed, rocket->velocity); rocket->movetype = MOVETYPE_FLYMISSILE; rocket->clipmask = MASK_SHOT; rocket->solid = SOLID_BBOX; VectorClear (rocket->mins); VectorClear (rocket->maxs); if (sv_serversideonly->value) { rocket->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2"); rocket->s.effects = EF_ROCKET; } else { rocket->s.modelindex2 = gi.modelindex ("models/objects/rocket/tris.md2"); rocket->s.modelindex = gi.modelindex ("models/objects/rocket_flame/tris.md2"); rocket->s.effects = EF_GRENADE | EF_PLASMA; } rocket->owner = self; rocket->touch = rocket_touch; rocket->nextthink = level.time + 8000/speed; rocket->think = Rocket_Explode; rocket->dmg = damage; rocket->radius_dmg = radius_damage; rocket->dmg_radius = damage_radius; rocket->s.sound = gi.soundindex ("weapons/rockfly.wav"); rocket->classname = "rocket"; if (self->client) if (self->client->rock_set == 1) { rocket->movetype = MOVETYPE_TOSS; rocket->nextthink = level.time; rocket->think = Rocket_Tilt; VectorAdd(rocket->velocity, self->velocity, rocket->velocity); } if (self->client) if (self->client->laser_on) { rocket->linkedto = self->client->laser_targeted; rocket->movetype = MOVETYPE_FLYMISSILE; rocket->nextthink = level.time; rocket->think = Rocket_Tilt; } // CCH: a few more attributes to let the rocket 'die' VectorSet(rocket->mins, -10, -3, 0); VectorSet(rocket->maxs, 10, 3, 6); rocket->mass = 10; rocket->health = 1; rocket->die = Rocket_Die; rocket->takedamage = DAMAGE_YES; rocket->monsterinfo.aiflags = AI_NOSTEP; rocket->s.renderfx |= RF_IR_VISIBLE; if (self->client) check_dodge (self, rocket->s.origin, dir, speed); gi.linkentity (rocket); } void fire_rocket_bfg (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage) { edict_t *rocket; rocket = G_Spawn(); VectorCopy (start, rocket->s.origin); VectorCopy (dir, rocket->movedir); vectoangles (dir, rocket->s.angles); VectorScale (dir, speed, rocket->velocity); rocket->movetype = MOVETYPE_TOSS; //MOVETYPE_FLYMISSILE; rocket->clipmask = MASK_SHOT; rocket->solid = SOLID_BBOX; VectorClear (rocket->mins); VectorClear (rocket->maxs); if (sv_serversideonly->value) { rocket->s.modelindex = gi.modelindex ("models/objects/bomb/tris.md2"); rocket->nextthink = level.time; rocket->think = Rocket_Tilt; rocket->s.effects = EF_GRENADE; } else { rocket->s.effects = EF_PLASMA; rocket->s.renderfx = RF_FULLBRIGHT; rocket->s.modelindex = gi.modelindex ("models/objects/ion/tris.md2"); VectorSet (rocket->avelocity, 300, 300, 300); } rocket->owner = self; rocket->touch = rocket_bfg_touch; rocket->dmg = damage; rocket->radius_dmg = radius_damage; rocket->dmg_radius = damage_radius; rocket->s.sound = gi.soundindex ("weapons/rockfly.wav"); rocket->classname = "rocket"; // CCH: a few more attributes to let the rocket 'die' VectorSet(rocket->mins, -10, -3, 0); VectorSet(rocket->maxs, 10, 3, 6); rocket->mass = 50; rocket->health = 1; rocket->die = Rocket_Die; rocket->takedamage = DAMAGE_YES; rocket->monsterinfo.aiflags = AI_NOSTEP; rocket->s.renderfx |= RF_IR_VISIBLE; rocket->movetype = MOVETYPE_TOSS; if (self->client) { check_dodge (self, rocket->s.origin, dir, speed); if (self->client->bfg_set==1) { rocket->movetype = MOVETYPE_FLY; rocket->think = Rocket_Camera; VectorScale (dir, 50, rocket->velocity); } } gi.linkentity (rocket); } void fire_rocket_2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage) { edict_t *rocket; rocket = G_Spawn(); VectorCopy (start, rocket->s.origin); VectorCopy (dir, rocket->movedir); vectoangles (dir, rocket->s.angles); VectorScale (dir, speed, rocket->velocity); rocket->movetype = MOVETYPE_TOSS; rocket->clipmask = MASK_SHOT; rocket->solid = SOLID_BBOX; rocket->s.effects |= EF_ROCKET; VectorClear (rocket->mins); VectorClear (rocket->maxs); rocket->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2"); rocket->owner = self; rocket->touch = rocket_touch; rocket->nextthink = level.time + 8000/speed; rocket->think = G_FreeEdict; rocket->dmg = damage; rocket->radius_dmg = radius_damage; rocket->dmg_radius = damage_radius; rocket->s.sound = gi.soundindex ("weapons/rockfly.wav"); rocket->classname = "rocket"; // CCH: a few more attributes to let the rocket 'die' VectorSet(rocket->mins, -10, -3, 0); VectorSet(rocket->maxs, 10, 3, 6); rocket->mass = 10; rocket->health = 1; rocket->die = Rocket_Die; rocket->takedamage = DAMAGE_YES; rocket->monsterinfo.aiflags = AI_NOSTEP; if (self->client) check_dodge (self, rocket->s.origin, dir, speed); gi.linkentity (rocket); } /* ================= fire_rail ================= */ void fire_rail (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick) { vec3_t from; vec3_t end; trace_t tr; edict_t *ignore; int mask, counter=0; qboolean water; VectorMA (start, 8102, aimdir, end); VectorCopy (start, from); ignore = self; water = false; mask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA; if (!deathmatch->value&&!coop->value || sv_realbullets->value) { //tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT); fire_bullet_slow (self, start, aimdir, sv_maxvelocity->value, 222, damage, MOD_RAILGUN, 0); return; } while (ignore && counter < RAILCHECKMAX) { counter++; tr = gi.trace (from, NULL, NULL, end, ignore, mask); if (tr.contents & (CONTENTS_SLIME|CONTENTS_LAVA)) { mask &= ~(CONTENTS_SLIME|CONTENTS_LAVA); water = true; } else { //ZOID--added so rail goes through SOLID_BBOX entities (gibs, etc) if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client) || (tr.ent->solid == SOLID_BBOX)) ignore = tr.ent; else ignore = NULL; if ((tr.ent != self) && (tr.ent->takedamage)) T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN); } VectorCopy (tr.endpos, from); } // send gun puff / flash gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_RAILTRAIL); gi.WritePosition (start); gi.WritePosition (tr.endpos); gi.multicast (self->s.origin, MULTICAST_PHS); // gi.multicast (start, MULTICAST_PHS); if (water) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_RAILTRAIL); gi.WritePosition (start); gi.WritePosition (tr.endpos); gi.multicast (tr.endpos, MULTICAST_PHS); } BulletMark (self, &tr, MOD_RAILGUN); if (self->client) PlayerNoise(self, tr.endpos, PNOISE_IMPACT); } /* ================= fire_rail_2 ================= */ void fire_rail_2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick) { vec3_t from; vec3_t end; trace_t tr; edict_t *ignore; int mask; qboolean water; int effect, effect2; VectorMA (start, 8192, aimdir, end); VectorCopy (start, from); ignore = self; water = false; mask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA; tr = gi.trace (from, NULL, NULL, end, ignore, mask); if (tr.contents & (CONTENTS_SLIME|CONTENTS_LAVA)) { mask &= ~(CONTENTS_SLIME|CONTENTS_LAVA); water = true; } else { if ((tr.ent != self) && (tr.ent->takedamage)) T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN); } VectorCopy (tr.endpos, from); gi.sound (self, CHAN_WEAPON, gi.soundindex("weapons/laser2.wav"), 1, ATTN_NORM, 0); fire_laser (self, start, aimdir, 0, 2, 100); fire_laser (self, start, aimdir, 9, 4, 75); fire_laser (self, start, aimdir, 10, 6, 50); } /* ================= fire_bfg ================= */ void bfg_explode (edict_t *self) { edict_t *ent; float points; vec3_t v; float dist; if (self->s.frame == 0) { // the BFG effect ent = NULL; while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL) { if (!ent->takedamage) continue; if (ent == self->owner) continue; if (!CanDamage (ent, self)) continue; if (!CanDamage (ent, self->owner)) continue; VectorAdd (ent->mins, ent->maxs, v); VectorMA (ent->s.origin, 0.5, v, v); VectorSubtract (self->s.origin, v, v); dist = VectorLength(v); points = self->radius_dmg * (1.0 - sqrt(dist/self->dmg_radius)); if (ent == self->owner) points = points * 0.5; gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BFG_EXPLOSION); gi.WritePosition (ent->s.origin); gi.multicast (ent->s.origin, MULTICAST_PHS); T_Damage (ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT); } } self->nextthink = level.time + FRAMETIME; self->s.frame++; if (self->s.frame == 5) self->think = G_FreeEdict; } void bfg_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { if (other == self->owner) return; if (surf && (surf->flags & SURF_SKY)) { G_FreeEdict (self); return; } if (self->owner->client) PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); // core explosion - prevents firing it into the wall/floor if (other->takedamage) T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST); T_RadiusDamage(self, self->owner, 200, other, 100, MOD_BFG_BLAST); gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0); self->solid = SOLID_NOT; self->touch = NULL; VectorMA (self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin); VectorClear (self->velocity); self->s.modelindex = gi.modelindex ("sprites/s_bfg3.sp2"); self->s.frame = 0; self->s.sound = 0; self->s.effects &= ~EF_ANIM_ALLFAST; self->think = bfg_explode; self->nextthink = level.time + FRAMETIME; self->enemy = other; gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BFG_BIGEXPLOSION); gi.WritePosition (self->s.origin); gi.multicast (self->s.origin, MULTICAST_PVS); } void bfg_think (edict_t *self) { edict_t *ent; edict_t *ignore; vec3_t point; vec3_t dir; vec3_t start; vec3_t end; int dmg; trace_t tr; if (deathmatch->value) dmg = 5; else dmg = 10; ent = NULL; while ((ent = findradius(ent, self->s.origin, 256)) != NULL) { if (ent == self) continue; if (ent == self->owner) continue; if (!ent->takedamage) continue; if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0)) continue; VectorMA (ent->absmin, 0.5, ent->size, point); VectorSubtract (point, self->s.origin, dir); VectorNormalize (dir); ignore = self; VectorCopy (self->s.origin, start); VectorMA (start, 2048, dir, end); while(1) { tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER); if (!tr.ent) break; // hurt it if we can if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner)) T_Damage (tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER); // if we hit something that's not a monster or player we're done if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_LASER_SPARKS); gi.WriteByte (4); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.WriteByte (self->s.skinnum); gi.multicast (tr.endpos, MULTICAST_PVS); break; } ignore = tr.ent; VectorCopy (tr.endpos, start); } gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BFG_LASER); gi.WritePosition (self->s.origin); gi.WritePosition (tr.endpos); gi.multicast (self->s.origin, MULTICAST_PHS); } self->nextthink = level.time + FRAMETIME; } void fire_bfg (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius) { edict_t *bfg; bfg = G_Spawn(); VectorCopy (start, bfg->s.origin); VectorCopy (dir, bfg->movedir); vectoangles (dir, bfg->s.angles); VectorScale (dir, speed, bfg->velocity); bfg->movetype = MOVETYPE_FLYMISSILE; bfg->clipmask = MASK_SHOT; bfg->solid = SOLID_BBOX; bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST; VectorClear (bfg->mins); VectorClear (bfg->maxs); bfg->s.modelindex = gi.modelindex ("sprites/s_bfg1.sp2"); bfg->owner = self; bfg->touch = bfg_touch; bfg->nextthink = level.time + 8000/speed; bfg->think = G_FreeEdict; bfg->radius_dmg = damage; bfg->dmg_radius = damage_radius; bfg->classname = "bfg blast"; bfg->s.sound = gi.soundindex ("weapons/bfg__l1a.wav"); bfg->think = bfg_think; bfg->nextthink = level.time + FRAMETIME; bfg->teammaster = bfg; bfg->teamchain = NULL; if (self->client) check_dodge (self, bfg->s.origin, dir, speed); gi.linkentity (bfg); } /************************************************************************************* SLOW MO BULLETS *************************************************************************************/ #define RAILTRAILTYPE TE_BUBBLETRAIL2 void SlowBulletThink (edict_t *ent); void SlowBulletDie (edict_t *self) { if (self->health == 333) { tempent (TE_ROCKET_EXPLOSION, self->s.origin, NULL); } G_FreeEdict(self); } void SlowBulletTouch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { int mod, i=0, size; vec3_t temp, temp2, temp3; trace_t tr; int speed = sv_maxvelocity->value; if (GameSlowMo) speed *= SLOWMOMULT; AngleVectors (self->s.angles, temp3, NULL, NULL); VectorNormalize(temp3); if (other == self->owner) return; if (other == self->linkedto) return; if (other == self->owner->target_ent) return; if ( (surf && (surf->flags & SURF_SKY)) || (other->solid == SOLID_TRIGGER) ) { SlowBulletDie (self); return; } if (self->owner->client) PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); mod = self->mod; if (other->takedamage) { T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, self->health, 0, self->mod); if (!(self->health == 222 || self->health == 333 || self->mod==MOD_MAGIC)) SlowBulletDie(self); else { self->linkedto = other; VectorScale(temp3, speed , self->velocity); VectorMA (self->s.origin, sv_maxvelocity->value/10 , temp3, temp); tr = gi.trace (self->s.origin, self->mins, self->maxs, temp, other, MASK_SHOT); VectorCopy(tr.endpos,temp2); tr = gi.trace (temp2, NULL, NULL, self->s.origin, self, MASK_SHOT); VectorMA (tr.endpos, 100, temp3, temp); tr = gi.trace (tr.endpos, NULL, NULL, temp, other, MASK_SHOT); gi.WriteByte (svc_temp_entity); gi.WriteByte (self->grappleType); gi.WritePosition (self->s.old_origin); gi.WritePosition (tr.endpos); gi.multicast (self->s.old_origin, MULTICAST_PHS); VectorCopy(tr.endpos,self->s.origin); self->think(self); } } else { if (self->health==222) { if (plane) BulletMarkSlow (self, plane->normal, other); G_FreeEdict (self); } else { if (plane) { VectorCopy (vec3_origin , self->end_pt); BulletMarkSlow (self, plane->normal, other); } SlowBulletDie(self); } } } void SlowBulletThink (edict_t *ent) { int TRAILLENGTH = sv_maxvelocity->value/10; int i, speed = sv_maxvelocity->value; vec3_t temp; trace_t tr; if (GameSlowMo) speed *= SLOWMOMULT; AngleVectors (ent->s.angles, temp, NULL, NULL); VectorNormalize(temp); VectorScale(temp, speed , ent->velocity); if (ent->waterlevel||sv_waterlevel->value) ent->s.effects |= EF_GRENADE; else ent->s.effects = 0; if (ent->health == 222) { VectorCopy(ent->velocity, temp); VectorNormalize(temp); VectorMA (ent->s.origin, TRAILLENGTH, temp, temp); tr = gi.trace (ent->s.origin, NULL, NULL, temp, ent, MASK_SOLID); gi.WriteByte (svc_temp_entity); gi.WriteByte (ent->grappleType); gi.WritePosition (ent->s.origin); gi.WritePosition (tr.endpos); gi.multicast (ent->s.origin, MULTICAST_PHS); } ent->nextthink = level.time; } void fire_bullet_slow (edict_t *self, vec3_t start, vec3_t dir, int speed, int kick, int damage, int mod, int effect) { edict_t *bolt; trace_t tr; int i; if (GameSlowMo) speed *= SLOWMOMULT; VectorNormalize (dir); bolt = G_Spawn(); bolt->svflags = SVF_DEADMONSTER; VectorCopy (start, bolt->s.origin); VectorCopy (start, bolt->s.old_origin); vectoangles (dir, bolt->s.angles); VectorScale (dir, speed, bolt->velocity); bolt->movetype = MOVETYPE_FLYMISSILE; bolt->clipmask = MASK_SHOT; bolt->solid = SOLID_BBOX; VectorClear (bolt->mins); VectorClear (bolt->maxs); bolt->owner = self; bolt->touch = SlowBulletTouch; bolt->nextthink = level.time; bolt->think = SlowBulletThink; bolt->dmg = damage; bolt->classname = "bolt"; bolt->mod = mod; bolt->timer = effect; bolt->linkedto = NULL; if (kick == 222) bolt->grappleType = TE_RAILTRAIL; else if (kick == 333 || mod==MOD_MAGIC) { bolt->grappleType = TE_BUBBLETRAIL2; kick = 222; } else if (kick == 444) { bolt->grappleType = TE_BFG_LASER; kick = 222; } bolt->health = kick; bolt->s.renderfx |= RF_TRANSLUCENT; bolt->s.effects = 0; if ((sv_waterlevel->value)&&(kick!=222)) bolt->s.effects |= EF_GRENADE; if (sv_serversideonly->value) bolt->s.modelindex = gi.modelindex ("models/objects/laser/tris.md2"); else bolt->s.modelindex = gi.modelindex ("models/objects/tracr/tris.md2"); bolt->spawnflags = 1; gi.linkentity (bolt); if (self->client) check_dodge (self, bolt->s.origin, dir, speed); if (bolt->health==222) { SlowBulletThink(bolt); bolt->s.renderfx = RF_BEAM; } /* tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT); if (tr.fraction < 1.0) { VectorMA (bolt->s.origin, -10, dir, bolt->s.origin); bolt->touch (bolt, tr.ent, NULL, NULL); }*/ }