/* * Copyright(c) 1997-2001 Id Software, Inc. * Copyright(c) 2002 The Quakeforge Project. * Copyright(c) 2006 Quetoo. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or(at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "client.h" #define MAX_BEAMS 32 typedef struct { int entity; int dest_entity; struct model_s *model; int endtime; vec3_t offset; vec3_t start, end; } beam_t; beam_t cl_beams[MAX_BEAMS]; #define MAX_LASERS 32 typedef struct { entity_t ent; int endtime; } laser_t; laser_t cl_lasers[MAX_LASERS]; void CL_BlasterParticles(vec3_t org, vec3_t dir); void CL_ExplosionParticles(vec3_t org); void CL_BFGExplosionParticles(vec3_t org); struct sfx_s *cl_sfx_ric1; struct sfx_s *cl_sfx_ric2; struct sfx_s *cl_sfx_ric3; struct sfx_s *cl_sfx_lashit; struct sfx_s *cl_sfx_spark5; struct sfx_s *cl_sfx_spark6; struct sfx_s *cl_sfx_spark7; struct sfx_s *cl_sfx_railg; struct sfx_s *cl_sfx_rockexp; struct sfx_s *cl_sfx_grenexp; struct sfx_s *cl_sfx_watrexp; struct sfx_s *cl_sfx_footsteps[4]; struct model_s *cl_mod_parasite_segment; struct model_s *cl_mod_parasite_tip; struct model_s *cl_mod_grapple_cable; struct model_s *cl_mod_powerscreen; /* CL_RegisterTEntSounds */ void CL_RegisterTEntSounds(void){ int i; char name[MAX_QPATH]; cl_sfx_ric1 = S_RegisterSound("world/ric1.wav"); cl_sfx_ric2 = S_RegisterSound("world/ric2.wav"); cl_sfx_ric3 = S_RegisterSound("world/ric3.wav"); cl_sfx_lashit = S_RegisterSound("weapons/lashit.wav"); cl_sfx_spark5 = S_RegisterSound("world/spark5.wav"); cl_sfx_spark6 = S_RegisterSound("world/spark6.wav"); cl_sfx_spark7 = S_RegisterSound("world/spark7.wav"); cl_sfx_railg = S_RegisterSound("weapons/railgf1a.wav"); cl_sfx_rockexp = S_RegisterSound("weapons/rocklx1a.wav"); cl_sfx_grenexp = S_RegisterSound("weapons/grenlx1a.wav"); cl_sfx_watrexp = S_RegisterSound("weapons/xpld_wat.wav"); S_RegisterSound("player/land1.wav"); S_RegisterSound("player/fall2.wav"); S_RegisterSound("player/fall1.wav"); for(i = 0; i < 4; i++){ Com_sprintf(name, sizeof(name), "player/step%i.wav", i + 1); cl_sfx_footsteps[i] = S_RegisterSound(name); } } /* CL_RegisterTEntModels */ void CL_RegisterTEntModels(void){ cl_mod_parasite_segment = GL_RegisterModel("models/monsters/parasite/segment/tris.md2"); cl_mod_parasite_tip = GL_RegisterModel("models/monsters/parasite/tip/tris.md2"); cl_mod_grapple_cable = GL_RegisterModel("models/ctf/segment/tris.md2"); cl_mod_powerscreen = GL_RegisterModel("models/items/armor/effect/tris.md2"); GL_RegisterModel("models/objects/laser/tris.md2"); GL_RegisterModel("models/objects/grenade2/tris.md2"); GL_RegisterModel("models/weapons/v_machn/tris.md2"); GL_RegisterModel("models/weapons/v_handgr/tris.md2"); GL_RegisterModel("models/weapons/v_shotg2/tris.md2"); GL_RegisterModel("models/objects/gibs/bone/tris.md2"); GL_RegisterModel("models/objects/gibs/sm_meat/tris.md2"); GL_RegisterModel("models/objects/gibs/bone2/tris.md2"); Draw_FindPic("w_machinegun"); Draw_FindPic("a_bullets"); Draw_FindPic("i_health"); Draw_FindPic("a_grenades"); } /* CL_ClearTEnts */ void CL_ClearTEnts(void){ memset(cl_beams, 0, sizeof(cl_beams)); memset(cl_lasers, 0, sizeof(cl_lasers)); } /* CL_ParseParticles */ void CL_ParseParticles(void){ int color, count; vec3_t pos, dir; MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); color = MSG_ReadByte(&net_message); count = MSG_ReadByte(&net_message); CL_ParticleEffect(pos, dir, color, count); } /* CL_ParseBeam */ int CL_ParseBeam(struct model_s *model){ int ent; vec3_t start, end; beam_t *b; int i; ent = MSG_ReadShort(&net_message); MSG_ReadPos(&net_message, start); MSG_ReadPos(&net_message, end); // override any beam with the same entity for(i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++) if(b->entity == ent){ b->entity = ent; b->model = model; b->endtime = cl.time + 200; VectorCopy(start, b->start); VectorCopy(end, b->end); VectorClear(b->offset); return ent; } // find a free beam for(i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++){ if(!b->model || b->endtime < cl.time){ b->entity = ent; b->model = model; b->endtime = cl.time + 200; VectorCopy(start, b->start); VectorCopy(end, b->end); VectorClear(b->offset); return ent; } } Com_Printf("beam list overflow!\n"); return ent; } /* CL_ParseBeam2 */ int CL_ParseBeam2(struct model_s *model){ int ent; vec3_t start, end, offset; beam_t *b; int i; ent = MSG_ReadShort(&net_message); MSG_ReadPos(&net_message, start); MSG_ReadPos(&net_message, end); MSG_ReadPos(&net_message, offset); // override any beam with the same entity for(i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++) if(b->entity == ent){ b->entity = ent; b->model = model; b->endtime = cl.time + 200; VectorCopy(start, b->start); VectorCopy(end, b->end); VectorCopy(offset, b->offset); return ent; } // find a free beam for(i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++){ if(!b->model || b->endtime < cl.time){ b->entity = ent; b->model = model; b->endtime = cl.time + 200; VectorCopy(start, b->start); VectorCopy(end, b->end); VectorCopy(offset, b->offset); return ent; } } Com_Printf("beam list overflow!\n"); return ent; } /* CL_ParseLaser */ void CL_ParseLaser(int colors){ vec3_t start; vec3_t end; laser_t *l; int i; MSG_ReadPos(&net_message, start); MSG_ReadPos(&net_message, end); for(i = 0, l = cl_lasers; i < MAX_LASERS; i++, l++){ if(l->endtime < cl.time){ l->ent.flags = RF_TRANSLUCENT | RF_BEAM; VectorCopy(start, l->ent.origin); VectorCopy(end, l->ent.oldorigin); l->ent.alpha = 0.30; l->ent.skinnum =(colors >>((rand() % 4) * 8)) & 0xff; l->ent.model = NULL; l->ent.frame = 4; l->endtime = cl.time + 100; return; } } } /* CL_ParseTEnt */ static byte splash_color[] = {0x00, 0xe0, 0xb0, 0x50, 0xd0, 0xe0, 0xe8}; void CL_ParseTEnt(void){ int type; vec3_t pos, pos2, dir; int cnt; int color; int r; int ent; type = MSG_ReadByte(&net_message); switch(type){ case TE_BLOOD: // bullet hitting flesh MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); CL_ParticleEffect(pos, dir, 0xe8, 20); break; case TE_GUNSHOT: // bullet hitting wall case TE_SPARKS: case TE_BULLET_SPARKS: MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); if(type == TE_GUNSHOT) CL_ParticleEffect(pos, dir, 0, 10); else CL_ParticleEffect(pos, dir, 0xe0, 6); if(type != TE_SPARKS){ // impact sound cnt = rand() & 15; if(cnt == 1) S_StartSound(pos, 0, 0, cl_sfx_ric1, 1, ATTN_NORM, 0); else if(cnt == 2) S_StartSound(pos, 0, 0, cl_sfx_ric2, 1, ATTN_NORM, 0); else if(cnt == 3) S_StartSound(pos, 0, 0, cl_sfx_ric3, 1, ATTN_NORM, 0); } break; case TE_SCREEN_SPARKS: case TE_SHIELD_SPARKS: MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); if(type == TE_SCREEN_SPARKS) CL_ParticleEffect(pos, dir, 0xd0, 30); else CL_ParticleEffect(pos, dir, 0xb0, 30); S_StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); break; case TE_SHOTGUN: // bullet hitting wall MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); CL_ParticleEffect(pos, dir, 0, 5); break; case TE_SPLASH: // bullet hitting water cnt = MSG_ReadByte(&net_message); MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); r = MSG_ReadByte(&net_message); if(r > 6) color = 0x00; else color = splash_color[r]; CL_ParticleEffect(pos, dir, color, cnt); if(r == SPLASH_SPARKS){ r = rand() & 3; if(r == 0) S_StartSound(pos, 0, 0, cl_sfx_spark5, 1, ATTN_STATIC, 0); else if(r == 1) S_StartSound(pos, 0, 0, cl_sfx_spark6, 1, ATTN_STATIC, 0); else S_StartSound(pos, 0, 0, cl_sfx_spark7, 1, ATTN_STATIC, 0); } break; case TE_LASER_SPARKS: cnt = MSG_ReadByte(&net_message); MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); color = MSG_ReadByte(&net_message); CL_ParticleEffect2(pos, dir, color, cnt); break; case TE_BLASTER: // blaster hitting wall MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); CL_BlasterParticles(pos, dir); S_StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); break; case TE_RAILTRAIL: // railgun effect MSG_ReadPos(&net_message, pos); MSG_ReadPos(&net_message, pos2); CL_RailTrail(pos, pos2); S_StartSound(pos2, 0, 0, cl_sfx_railg, 1, ATTN_NORM, 0); break; case TE_EXPLOSION2: case TE_GRENADE_EXPLOSION: case TE_GRENADE_EXPLOSION_WATER: MSG_ReadPos(&net_message, pos); CL_ExplosionParticles(pos); if(type == TE_GRENADE_EXPLOSION_WATER) S_StartSound(pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0); else S_StartSound(pos, 0, 0, cl_sfx_grenexp, 1, ATTN_NORM, 0); break; case TE_EXPLOSION1: case TE_ROCKET_EXPLOSION: case TE_ROCKET_EXPLOSION_WATER: MSG_ReadPos(&net_message, pos); CL_ExplosionParticles(pos); if(type == TE_ROCKET_EXPLOSION_WATER) S_StartSound(pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0); else S_StartSound(pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0); break; case TE_BFG_EXPLOSION: MSG_ReadPos(&net_message, pos); break; case TE_BFG_BIGEXPLOSION: MSG_ReadPos(&net_message, pos); CL_BFGExplosionParticles(pos); break; case TE_BFG_LASER: CL_ParseLaser(0xd0d1d2d3); break; case TE_BUBBLETRAIL: MSG_ReadPos(&net_message, pos); MSG_ReadPos(&net_message, pos2); CL_BubbleTrail(pos, pos2); break; case TE_PARASITE_ATTACK: case TE_MEDIC_CABLE_ATTACK: ent = CL_ParseBeam(cl_mod_parasite_segment); break; case TE_BOSSTPORT: // boss teleporting to station MSG_ReadPos(&net_message, pos); CL_BigTeleportParticles(pos); S_StartSound(pos, 0, 0, S_RegisterSound("misc/bigtele.wav"), 1, ATTN_NONE, 0); break; case TE_GRAPPLE_CABLE: ent = CL_ParseBeam2(cl_mod_grapple_cable); break; default: Com_Printf("CL_ParseTEnt: unknown type"); break; } } /* CL_AddTEnts */ void CL_AddTEnts(void){ laser_t *l; beam_t *b; vec3_t dist, org; float yaw, pitch; float forward, len, d, steps; entity_t ent; int i, j; for(i = 0, l = cl_lasers; i < MAX_LASERS; i++, l++){ if(l->endtime >= cl.time) V_AddEntity(&l->ent); } for(i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++){ if(!b->model || b->endtime < cl.time) continue; if(b->entity == cl.playernum + 1){ // update origin to player VectorCopy(cl.viewdef.vieworg, b->start); b->start[2] -= 22; // adjust for view height } VectorAdd(b->start, b->offset, org); VectorSubtract(b->end, org, dist); if(dist[1] == 0 && dist[0] == 0){ yaw = 0; if(dist[2] > 0) pitch = 90; else pitch = 270; } else { if(dist[0]) yaw = (atan2(dist[1], dist[0]) * 180 / M_PI); else if(dist[1] > 0) yaw = 90; else yaw = 270; if(yaw < 0) yaw += 360; forward = sqrt(dist[0] * dist[0] + dist[1] * dist[1]); pitch = (atan2(dist[2], forward) * -180.0 / M_PI); if(pitch < 0) pitch += 360.0; } // add new entities for the beams d = VectorNormalize(dist); memset(&ent, 0, sizeof(ent)); steps = ceil(d / 30.0); len = (d - 30.0) /(steps - 1); while(d > 0){ VectorCopy(org, ent.origin); ent.model = b->model; ent.angles[0] = pitch; ent.angles[1] = yaw; ent.angles[2] = rand() % 360; V_AddEntity(&ent); for(j = 0; j < 3; j++) org[j] += dist[j] * len; d -= 30.0; } } }