/* =========================================================================== Copyright (C) 2011 su44 This file is part of OpenMoHAA source code. OpenMoHAA source code 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. OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // cg_parsemsg.c #include "cg_local.h" #define MAX_HDELEMENTS 256 hdelement_t hdelements[MAX_HDELEMENTS]; typedef struct { float i_vBarrel[3]; float i_vStart[3]; float (*i_vEnd)[3]; int i_iNumBullets; qboolean iLarge; int iTracerVisible; qboolean bIgnoreEntities; } bullet_tracer_t; #define MAX_IMPACTS 64 #define MAX_BULLER_TRACERS 32 #define MAX_BULLET_TRACE_BULLETS 1024 // bullet tracers static bullet_tracer_t bullet_tracers[MAX_BULLER_TRACERS]; static int bullet_tracers_count; static float bullet_tracer_bullets[MAX_BULLET_TRACE_BULLETS][3]; static int bullet_tracer_bullets_count; // wall impacts static vec3_t wall_impact_pos[MAX_IMPACTS]; static vec3_t wall_impact_norm[MAX_IMPACTS]; static int wall_impact_large[MAX_IMPACTS]; static int wall_impact_type[MAX_IMPACTS]; // 0,2,3,4,5 static int wall_impact_count; // flesh impacts static vec3_t flesh_impact_pos[MAX_IMPACTS]; static vec3_t flesh_impact_norm[MAX_IMPACTS]; static int flesh_impact_large[MAX_IMPACTS]; static int flesh_impact_type[MAX_IMPACTS]; static int flesh_impact_count; static int current_entity_number; void CG_MakeBulletHole(float *i_vPos, float *i_vNorm, int iLarge, trace_t *pPreTrace, qboolean bMakeSound) { char *s; #if 0 // su44: make an "explosion" effect here so I can debug bullet holes coordinates CG_MakeExplosion(i_vPos,vec3_origin,0,cgi.R_RegisterShader("heavy_pipe"),250,qtrue); #endif s = "bhole_"; // TODO: choose an appropriate decal shader and play hit sound CG_ImpactMark(cgi.R_RegisterShader("bhole_wood"),i_vPos,i_vNorm,0,1,1,1,1,qfalse,8,qfalse); } void CG_AddBulletImpacts() { int i; char *s; int type; for(i = 0; i < wall_impact_count; i++) { CG_MakeBulletHole(wall_impact_pos[i],wall_impact_norm[i], wall_impact_large[i],0,qtrue); type = wall_impact_type[i]; if(type) { if ( wall_impact_type[i] < 2 || wall_impact_type[i] > 3 ) s = "snd_bh_metal"; else s = "snd_bh_wood"; //CG_PlaySound(s,wall_impact_pos[i],-1, } } for(i = 0; i < flesh_impact_count; i++) { } } static void CG_MakeBubbleTrail(float *i_vStart, float *i_vEnd, int iLarge) { // TODO } // this is called from CG_MakeBulletTracerInternal static void CG_BulletTracerEffect(float *i_vStart, float *i_vEnd) { byte fTracerColor[4]; // su44: in MoHAA it was "float[4]" int iLife; vec3_t vNewStart; int v2; VectorCopy(i_vStart,vNewStart); v2 = 1; iLife = 20; fTracerColor[0] = 255; fTracerColor[1] = 255; fTracerColor[2] = 255; fTracerColor[3] = 255; CG_CreateBeam( vNewStart, // start vec3_origin, // dir 0, // owner 1, // model 1.0, // alpha 1.0, // scale 8192, // flags 1000.0, // len iLife, // life 1, // qboolean create i_vEnd, // endpointvec 0, // min_ofs 0, // maxs_ofs 0, // overlap 1, // numSubdivions 0, // delay "tracer", // beamshadername fTracerColor, // modulate (in FAKK that's a byte[4], in MoH that's float[4]) 0, // numSphereBeams 0.0, // sphereradius v2, //toggleDelay 1.0, //endapha 0, // renderfx "tracereffect"); // name } static void CG_MakeBulletTracerInternal(float *i_vBarrel, float *i_vStart, float (*i_vEnd)[3], int i_iNumBullets, qboolean iLarge, int iTracerVisible, qboolean bIgnoreEntities) { trace_t tr; int i; for(i = 0; i < i_iNumBullets; i++) { CG_BulletTracerEffect(i_vBarrel,i_vEnd[i]); CG_Trace(&tr,i_vBarrel,vec3_origin,vec3_origin,i_vEnd[i],-1,MASK_SHOT); if(tr.fraction != 1.f && tr.allsolid == qfalse) { CG_MakeBulletHole(tr.endpos,tr.plane.normal,iLarge,&tr,qfalse); } else { // su44: it happens quite often. Is it a bug? //CG_Printf("CG_MakeBulletTracerInternal: missed\n"); } } } static void CG_MakeBulletTracer(float *i_vBarrel, float *i_vStart, float (*i_vEnd)[3], int i_iNumBullets, qboolean iLarge, int iTracerVisible, qboolean bIgnoreEntities) { bullet_tracer_t *bt; int i; if(bullet_tracers_count < MAX_BULLER_TRACERS) { if(i_iNumBullets + bullet_tracer_bullets_count < MAX_BULLET_TRACE_BULLETS) { bt = bullet_tracers + bullet_tracers_count; VectorCopy(i_vBarrel,bt->i_vBarrel); VectorCopy(i_vStart,bt->i_vStart); bt->i_vEnd = &bullet_tracer_bullets[bullet_tracer_bullets_count]; bt->i_iNumBullets = i_iNumBullets; bt->iLarge = iLarge; bt->iTracerVisible = iTracerVisible; bt->bIgnoreEntities = bIgnoreEntities; bullet_tracers_count++; for(i = 0; i < i_iNumBullets; i++) { VectorCopy(i_vEnd[i],bullet_tracer_bullets[bullet_tracer_bullets_count]); bullet_tracer_bullets_count++; } } else { CG_Printf("CG_MakeBulletTracerInternal: MAX_BULLET_TRACE_BULLETS exceeded\n"); } } else { CG_Printf("CG_MakeBulletTracer: MAX_BULLER_TRACERS exceeded\n"); } } // su44: this should be called after CG_AddPacketEntities and before CG_DrawActive void CG_AddBulletTracers() { int i; bullet_tracer_t *bt; bt = bullet_tracers; for(i = 0; i < bullet_tracers_count; i++,bt++) { CG_MakeBulletTracerInternal(bt->i_vBarrel,bt->i_vStart,bt->i_vEnd,bt->i_iNumBullets, bt->iLarge,bt->iTracerVisible,bt->bIgnoreEntities); } bullet_tracers_count = 0; bullet_tracer_bullets_count = 0; } static void CG_MakeExplosionEffect(float *vPos, int iType) { CG_MakeExplosion(vPos,vec3_origin,0,cgi.R_RegisterShader("gren_explosion"),1000,qtrue); } static void CG_MakeEffect_Normal(int iEffect, vec3_t vPos, vec3_t vNormal) { // TODO } static void CG_SpawnEffectModel(const char *model, vec3_t origin) { localEntity_t *le; vec3_t spawnAngles = { 0, 0, 0 }; qhandle_t h; le = CG_AllocLocalEntity(); le->endTime = cg.time + 5000; le->leType = LE_SKIP; le->pos.trTime = cg.time - (rand()&15); VectorCopy( origin, le->pos.trDelta ); VectorCopy(origin,le->refEntity.origin); VectorCopy( spawnAngles, le->angles.trDelta ); AnglesToAxis(spawnAngles,le->refEntity.axis); h = cgi.R_RegisterModel(model); le->refEntity.model = h; le->leType = LE_FRAGMENT; le->tiki = cgi.TIKI_RegisterModel(model); } void CG_HudDrawElements() { int i; hdelement_t *hdi; //if ( cg_huddraw_force.integer || cg_hud.integer ) { hdi = hdelements; for ( i = 0; i < MAX_HDELEMENTS; i++, hdi++ ) { if ( hdi->hShader || hdi->string[0] ) { if ( hdi->vColor[3] != 0.f ) { float x = hdi->iX; float y = hdi->iY; float w = hdi->iWidth; float h = hdi->iHeight; if ( hdi->iHorizontalAlign == 1 ) { if ( hdi->bVirtualScreen ) x = 320.f - w * 0.5f + x; else x = cgs.glconfig.vidWidth * 0.5f - w * 0.5f + x; } else { if ( hdi->iHorizontalAlign == 2 ) { if ( hdi->bVirtualScreen ) x = x + 640.f; else x = cgs.glconfig.vidWidth + x; } } if ( hdi->iVerticalAlign == 1 ) { if ( hdi->bVirtualScreen ) y = 240.f - h * 0.5f + y; else y = cgs.glconfig.vidHeight * 0.5f - h * 0.5f + y; } else { if ( hdi->iVerticalAlign == 2 ) { if ( hdi->bVirtualScreen ) y = y + 480.f; else y = cgs.glconfig.vidHeight + y; } } cgi.R_SetColor(hdi->vColor); // su44: that's wrong if(hdi->bVirtualScreen) { if(x > 640) x -= 640; if(x < 0) x += 640; if(y > 480) y -= 480; if(y < 0) y += 480; } if ( hdi->string[0] ) { if ( hdi->pFont.glyphs[0].glyph ) { cgi.R_Text_Paint(&hdi->pFont, x, y, 1, 1, hdi->string,0,-1, qfalse, hdi->bVirtualScreen); } else { cgi.R_Text_Paint(&cgs.media.verdana, x, y, 1, 1, hdi->string,0,-1, qfalse, hdi->bVirtualScreen); } } else { if ( hdi->bVirtualScreen ) CG_AdjustFrom640(&x, &y, &w, &h); cgi.R_DrawStretchPic(x, y, w, h, 0.f, 0.f, 1.f, 1.f, hdi->hShader); } } } } } } static void CG_HudDrawShader (int iInfo) { hdelement_t *hdi = hdelements + iInfo; if(hdi->shaderName[0]) { hdi->hShader = cgi.R_RegisterShaderNoMip(hdi->shaderName); } else { hdi->hShader = 0; } } static void CG_HudDrawFont (int iInfo) { hdelement_t *hdi = hdelements + iInfo; if(hdi->fontName[0]) { cgi.R_RegisterFont(hdi->fontName,0,&hdi->pFont); } else { memset(&hdi->pFont,0,sizeof(hdi->pFont)); } } static void CG_PlaySound(char *sound_name, float *origin, int channel, float volume, float min_distance, float pitch, int argstype) { ubersound_t* sound; sound = CG_GetUbersound(sound_name); if(sound) cgi.S_StartSound( origin, -1, sound->channel, sound->sfxHandle ); } /* ===================== CG_ParseCGMessage ===================== */ void CG_ParseCGMessage() { int msgtype; vec3_t vecStart, vecTmp, vecEnd, vecArray[64]; int iCount, iLarge; int i, iTemp; char *s; hdelement_t *hde; // thats a hack, it should be done somewhere else [?] wall_impact_count = 0; flesh_impact_count = 0; do { msgtype = cgi.MSG_ReadBits( 6 ); if(cg_debugCGMessages->integer) { Com_Printf( "CG_ParseCGMessage: command type %i\n", msgtype ); } switch ( msgtype ) { case 1: // BulletTracer (visible?) vecTmp[0] = cgi.MSG_ReadCoord(); vecTmp[1] = cgi.MSG_ReadCoord(); vecTmp[2] = cgi.MSG_ReadCoord(); case 2: // BulletTracer (invisible?) case 5: // BubbleTrail vecStart[0] = cgi.MSG_ReadCoord(); vecStart[1] = cgi.MSG_ReadCoord(); vecStart[2] = cgi.MSG_ReadCoord(); if ( msgtype != 1 ) { vecTmp[0] = vecStart[0]; vecTmp[1] = vecStart[1]; vecTmp[2] = vecStart[2]; } vecArray[0][0] = cgi.MSG_ReadCoord(); vecArray[0][1] = cgi.MSG_ReadCoord(); vecArray[0][2] = cgi.MSG_ReadCoord(); iLarge = cgi.MSG_ReadBits( 1 ); if(msgtype==5) { CG_MakeBubbleTrail(vecStart,vecArray[0],iLarge); } else if(msgtype==1) { // su44: I am not sure about the last parameters... CG_MakeBulletTracer(vecTmp, vecStart, vecArray, 1, iLarge, 1, 1); } else { //msgtype == 2 CG_MakeBulletTracer(vecTmp, vecStart, vecArray, 1, iLarge, 0, 1); } break; case 3: // BulletTracer multiple times vecTmp[0] = cgi.MSG_ReadCoord(); vecTmp[1] = cgi.MSG_ReadCoord(); vecTmp[2] = cgi.MSG_ReadCoord(); iTemp = cgi.MSG_ReadBits( 6 ); case 4: // BulletTracer multiple times (shotgun shot) if ( msgtype == 4 ) iTemp = 0; vecStart[0] = cgi.MSG_ReadCoord(); vecStart[1] = cgi.MSG_ReadCoord(); vecStart[2] = cgi.MSG_ReadCoord(); iLarge = cgi.MSG_ReadBits( 1 ); iCount = cgi.MSG_ReadBits( 6 ); // this check is missing in MOHAA code, so this has buffer overflow risk in AA if ( iCount > 64 ) Com_Error( ERR_DROP, "CG message type 4 sent too many data.\n" ); for (i=0;ishaderName, cgi.MSG_ReadString(), sizeof(hde->shaderName) ); hde->string[0] = 0; memset(&hde->pFont,0,sizeof(hde->pFont)); hde->fontName[0] = 0; CG_HudDrawShader(i); break; case 28: // HUD drawing... i = cgi.MSG_ReadByte(); hde = hdelements + i; hde->iHorizontalAlign = cgi.MSG_ReadBits( 2 ); hde->iHorizontalAlign = cgi.MSG_ReadBits( 2 ); break; case 29: i = cgi.MSG_ReadByte(); hde = hdelements + i; hde->iX = cgi.MSG_ReadShort(); hde->iY = cgi.MSG_ReadShort(); hde->iWidth = cgi.MSG_ReadShort(); hde->iHeight = cgi.MSG_ReadShort(); break; case 30: i = cgi.MSG_ReadByte(); hde = hdelements + i; hde->bVirtualScreen = cgi.MSG_ReadBits( 1 ); break; case 31: // huddraw_color i = cgi.MSG_ReadByte(); hde = hdelements + i; hde->vColor[0] = cgi.MSG_ReadByte()*0.003921568859368563; hde->vColor[1] = cgi.MSG_ReadByte()*0.003921568859368563; hde->vColor[2] = cgi.MSG_ReadByte()*0.003921568859368563; break; case 32: // huddraw_alpha i = cgi.MSG_ReadByte(); hde = hdelements + i; hde->vColor[3] = cgi.MSG_ReadByte()*0.003921568859368563; break; case 33: // huddraw_string i = cgi.MSG_ReadByte(); hde = hdelements + i; hde->hShader = 0; Q_strncpyz( hde->string, cgi.MSG_ReadString(), sizeof(hde->string) ); break; case 34: // huddraw_font i = cgi.MSG_ReadByte(); hde = hdelements + i; Q_strncpyz( hde->fontName, cgi.MSG_ReadString(), sizeof(hde->fontName) ); hde->hShader = 0; hde->shaderName[0] = 0; CG_HudDrawFont(i); break; case 35: case 36: // TODO - play sound (?) //CG_PlaySound(s, 0, 5, 2.0, -1.0, vecStart, 1); break; case 37: // voicechat message (squad command, taunt, etc...) vecStart[0] = cgi.MSG_ReadCoord(); vecStart[1] = cgi.MSG_ReadCoord(); vecStart[2] = cgi.MSG_ReadCoord(); iTemp = cgi.MSG_ReadBits( 1 ); // read client index i = cgi.MSG_ReadBits( 6 ); // read voicechat sound alias s = cgi.MSG_ReadString(); if(iTemp) { current_entity_number = i; } //CG_Printf("Case 37: iTemp %i, i %i, s %s\n",iTemp,i,s); // play an aliased sound from uberdialog.scr // wombat: only if there is a sound to be played if ( *s ) CG_PlaySound(s,vecStart,5,-1.0,-1.0,-1.0,0); break; } } while ( cgi.MSG_ReadBits(1) ); }