/* * CG_Event_Pain */ void CG_Event_Pain( entity_state_t *state, int parm ) { if( parm == PAIN_WARSHELL ) { if( ISVIEWERENTITY( state->number ) ) trap_S_StartGlobalSound( CG_MediaSfx( cgs.media.sfxShellHit ), CHAN_PAIN, cg_volume_players->value ); else trap_S_StartRelativeSound( CG_MediaSfx( cgs.media.sfxShellHit ), state->number, CHAN_PAIN, cg_volume_players->value, ATTN_NORM ); } else { CG_SexedSound( state->number, CHAN_PAIN, va( S_PLAYER_PAINS, 25*( parm+1 ) ), cg_volume_players->value ); } switch( (int)brandom( 0, 3 ) ) { case 0: CG_PModel_AddAnimation( state->number, 0, TORSO_PAIN1, 0, EVENT_CHANNEL ); break; case 1: CG_PModel_AddAnimation( state->number, 0, TORSO_PAIN2, 0, EVENT_CHANNEL ); break; case 2: default: CG_PModel_AddAnimation( state->number, 0, TORSO_PAIN3, 0, EVENT_CHANNEL ); break; } }
//============== // CG_ForceTeam //============== static int CG_ForceTeam( int entNum, int team ) { if( !GS_TeamBasedGametype() ) { if( ISVIEWERENTITY( entNum ) ) { if( cg_forceMyTeamAlpha->integer ) return TEAM_ALPHA; } else { if( cg_forceTeamPlayersTeamBeta->integer ) return TEAM_BETA; } return team; } else { int myteam = cg.predictedPlayerState.stats[STAT_TEAM]; if( cg_forceMyTeamAlpha->integer && myteam != TEAM_SPECTATOR ) { if( team == myteam ) return TEAM_ALPHA; if( team == TEAM_ALPHA ) return myteam; } return team; } }
/* * CG_Event_Dash */ void CG_Event_Dash( entity_state_t *state, int parm ) { switch( parm ) { default: break; case 0: // dash front CG_PModel_AddAnimation( state->number, LEGS_DASH, 0, 0, EVENT_CHANNEL ); CG_SexedSound( state->number, CHAN_BODY, va( S_PLAYER_DASH_1_to_2, ( rand()&1 )+1 ), cg_volume_players->value ); break; case 1: // dash left CG_PModel_AddAnimation( state->number, LEGS_DASH_LEFT, 0, 0, EVENT_CHANNEL ); CG_SexedSound( state->number, CHAN_BODY, va( S_PLAYER_DASH_1_to_2, ( rand()&1 )+1 ), cg_volume_players->value ); break; case 2: // dash right CG_PModel_AddAnimation( state->number, LEGS_DASH_RIGHT, 0, 0, EVENT_CHANNEL ); CG_SexedSound( state->number, CHAN_BODY, va( S_PLAYER_DASH_1_to_2, ( rand()&1 )+1 ), cg_volume_players->value ); break; case 3: // dash back CG_PModel_AddAnimation( state->number, LEGS_DASH_BACK, 0, 0, EVENT_CHANNEL ); CG_SexedSound( state->number, CHAN_BODY, va( S_PLAYER_DASH_1_to_2, ( rand()&1 )+1 ), cg_volume_players->value ); break; } CG_Dash( state ); // Dash smoke effect // since most dash animations jump with right leg, reset the jump to start with left leg after a dash cg_entities[state->number].jumpedLeft = qtrue; //racesow - lm: filter out other players if( ISVIEWERENTITY( state->number )) CG_AddJumpspeed(); //!racesow }
/* * CG_Event_WallJump */ void CG_Event_WallJump( entity_state_t *state, int parm, int ev ) { vec3_t normal, forward, right; ByteToDir( parm, normal ); AngleVectors( tv( state->angles[0], state->angles[1], 0 ), forward, right, NULL ); if( DotProduct( normal, right ) > 0.3 ) CG_PModel_AddAnimation( state->number, LEGS_WALLJUMP_RIGHT, 0, 0, EVENT_CHANNEL ); else if( -DotProduct( normal, right ) > 0.3 ) CG_PModel_AddAnimation( state->number, LEGS_WALLJUMP_LEFT, 0, 0, EVENT_CHANNEL ); else if( -DotProduct( normal, forward ) > 0.3 ) CG_PModel_AddAnimation( state->number, LEGS_WALLJUMP_BACK, 0, 0, EVENT_CHANNEL ); else CG_PModel_AddAnimation( state->number, LEGS_WALLJUMP, 0, 0, EVENT_CHANNEL ); if( ev == EV_WALLJUMP_FAILED ) { if( ISVIEWERENTITY( state->number ) ) trap_S_StartGlobalSound( CG_MediaSfx( cgs.media.sfxWalljumpFailed ), CHAN_BODY, cg_volume_effects->value ); else trap_S_StartRelativeSound( CG_MediaSfx( cgs.media.sfxWalljumpFailed ), state->number, CHAN_BODY, cg_volume_effects->value, ATTN_NORM ); } else { CG_SexedSound( state->number, CHAN_BODY, va( S_PLAYER_WALLJUMP_1_to_2, ( rand()&1 )+1 ), cg_volume_players->value ); // smoke effect if( cg_cartoonEffects->integer & 1 ) { vec3_t pos; VectorCopy( state->origin, pos ); pos[2] += 15; CG_DustCircle( pos, normal, 65, 12 ); } } //racesow - lm: filter out other players if( ISVIEWERENTITY( state->number )) CG_AddJumpspeed(); //!racesow }
/* * CG_PModel_SpawnTeleportEffect */ void CG_PModel_SpawnTeleportEffect( centity_t *cent ) { int j; cgs_skeleton_t *skel; lentity_t *le; vec3_t teleportOrigin; vec3_t rgb; skel = CG_SkeletonForModel( cent->ent.model ); if( !skel || !cent->ent.boneposes ) return; for( j = LOCALEFFECT_EV_PLAYER_TELEPORT_IN; j <= LOCALEFFECT_EV_PLAYER_TELEPORT_OUT; j++ ) { if( cent->localEffects[j] ) { cent->localEffects[j] = 0; VectorSet( rgb, 0.5, 0.5, 0.5 ); if( j == LOCALEFFECT_EV_PLAYER_TELEPORT_OUT ) { VectorCopy( cent->teleportedFrom, teleportOrigin ); } else { VectorCopy( cent->teleportedTo, teleportOrigin ); if( ISVIEWERENTITY( cent->current.number ) ) { VectorSet( rgb, 0.1, 0.1, 0.1 ); } } // spawn a dummy model le = CG_AllocModel( LE_RGB_FADE, teleportOrigin, vec3_origin, 10, rgb[0], rgb[1], rgb[2], 1, 0, 0, 0, 0, cent->ent.model, CG_MediaShader( cgs.media.shaderTeleportShellGfx ) ); if( cent->skel ) { // use static bone pose, no animation le->skel = cent->skel; le->static_boneposes = ( bonepose_t * )CG_Malloc( sizeof( bonepose_t ) * le->skel->numBones ); memcpy( le->static_boneposes, cent->ent.boneposes, sizeof( bonepose_t ) * le->skel->numBones ); le->ent.boneposes = le->static_boneposes; le->ent.oldboneposes = le->ent.boneposes; } le->ent.frame = cent->ent.frame; le->ent.oldframe = cent->ent.oldframe; le->ent.backlerp = 1.0f; Matrix3_Copy( cent->ent.axis, le->ent.axis ); } } }
/* * CG_Event_Jump */ void CG_Event_Jump( entity_state_t *state, int parm ) { #define MOVEDIREPSILON 0.25f centity_t *cent; int xyspeedcheck; cent = &cg_entities[state->number]; xyspeedcheck = SQRTFAST( cent->animVelocity[0]*cent->animVelocity[0] + cent->animVelocity[1]*cent->animVelocity[1] ); if( xyspeedcheck < 100 ) { // the player is jumping on the same place, not running CG_PModel_AddAnimation( state->number, LEGS_JUMP_NEUTRAL, 0, 0, EVENT_CHANNEL ); CG_SexedSound( state->number, CHAN_BODY, va( S_PLAYER_JUMP_1_to_2, ( rand()&1 )+1 ), cg_volume_players->value ); } else { vec3_t movedir, viewaxis[3]; movedir[0] = cent->animVelocity[0]; movedir[1] = cent->animVelocity[1]; movedir[2] = 0; VectorNormalizeFast( movedir ); AngleVectors( tv( 0, cent->current.angles[YAW], 0 ), viewaxis[FORWARD], viewaxis[RIGHT], viewaxis[UP] ); // see what's his relative movement direction if( DotProduct( movedir, viewaxis[FORWARD] ) > MOVEDIREPSILON ) { cent->jumpedLeft = !cent->jumpedLeft; if( !cent->jumpedLeft ) { CG_PModel_AddAnimation( state->number, LEGS_JUMP_LEG2, 0, 0, EVENT_CHANNEL ); CG_SexedSound( state->number, CHAN_BODY, va( S_PLAYER_JUMP_1_to_2, ( rand()&1 )+1 ), cg_volume_players->value ); } else { CG_PModel_AddAnimation( state->number, LEGS_JUMP_LEG1, 0, 0, EVENT_CHANNEL ); CG_SexedSound( state->number, CHAN_BODY, va( S_PLAYER_JUMP_1_to_2, ( rand()&1 )+1 ), cg_volume_players->value ); } } else { CG_PModel_AddAnimation( state->number, LEGS_JUMP_NEUTRAL, 0, 0, EVENT_CHANNEL ); CG_SexedSound( state->number, CHAN_BODY, va( S_PLAYER_JUMP_1_to_2, ( rand()&1 )+1 ), cg_volume_players->value ); } } #undef MOVEDIREPSILON //racesow - lm: filter out other players if( ISVIEWERENTITY( state->number )) CG_AddJumpspeed(); //!racesow }
/* * CG_CalcViewBob */ static void CG_CalcViewBob( void ) { float bobMove, bobTime, bobScale; if( !cg.view.drawWeapon ) return; // calculate speed and cycle to be used for all cyclic walking effects cg.xyspeed = sqrt( cg.predictedPlayerState.pmove.velocity[0]*cg.predictedPlayerState.pmove.velocity[0] + cg.predictedPlayerState.pmove.velocity[1]*cg.predictedPlayerState.pmove.velocity[1] ); bobScale = 0; if( cg.xyspeed < 5 ) cg.oldBobTime = 0; // start at beginning of cycle again else if( cg_gunbob->integer ) { if( !ISVIEWERENTITY( cg.view.POVent ) ) bobScale = 0.0f; else if( CG_PointContents( cg.view.origin ) & MASK_WATER ) bobScale = 0.75f; else { centity_t *cent; vec3_t mins, maxs; trace_t trace; cent = &cg_entities[cg.view.POVent]; GS_BBoxForEntityState( ¢->current, mins, maxs ); maxs[2] = mins[2]; mins[2] -= ( 1.6f*STEPSIZE ); CG_Trace( &trace, cg.predictedPlayerState.pmove.origin, mins, maxs, cg.predictedPlayerState.pmove.origin, cg.view.POVent, MASK_PLAYERSOLID ); if( trace.startsolid || trace.allsolid ) { if( cg.predictedPlayerState.pmove.stats[PM_STAT_CROUCHTIME] ) bobScale = 1.5f; else bobScale = 2.5f; } } } bobMove = cg.frameTime * bobScale; bobTime = ( cg.oldBobTime += bobMove ); cg.bobCycle = (int)bobTime; cg.bobFracSin = fabs( sin( bobTime*M_PI ) ); }
/* * SCR_DrawPlayerTab */ static int SCR_DrawPlayerTab( const char **ptrptr, int team, int x, int y, int panelWidth, struct qfontface_s *font, int pass ) { int dir, align, i, columncount; char type, string[MAX_STRING_CHARS]; const char *token, *layout; int height, width, xoffset, yoffset; vec4_t teamcolor = { 0.0f, 0.0f, 0.0f, 1.0f }, color; int iconnum; struct shader_s *icon; bool highlight = false, trans = false; if( GS_TeamBasedGametype() ) { dir = ( team == TEAM_ALPHA ) ? -1 : 1; align = ( team == TEAM_ALPHA ) ? ALIGN_RIGHT_TOP : ALIGN_LEFT_TOP; } else { dir = 0; align = ALIGN_CENTER_TOP; } xoffset = 0; yoffset = 0; height = trap_SCR_FontHeight( font ); // start from the center again xoffset = CG_HorizontalAlignForWidth( 0, align, panelWidth ); xoffset += ( SCB_CENTERMARGIN * dir ); // draw the background columncount = 0; if( ( team == TEAM_ALPHA ) || ( team == TEAM_BETA ) ) CG_TeamColor( team, teamcolor ); // draw the player tab column titles layout = cgs.configStrings[CS_SCB_PLAYERTAB_LAYOUT]; while( SCR_GetNextColumnLayout( &layout, NULL, &type, &width, font ) != NULL ) { // grab the actual scoreboard data if( !SCR_ParseToken( ptrptr, &token ) ) break; if( SCR_SkipColumn( type ) ) continue; Vector4Copy( colorWhite, color ); // reset to white after each column icon = NULL; string[0] = 0; // interpret the data based on the type defined in the layout switch( type ) { default: CG_Error( "SCR_DrawPlayerTab: Invalid player tab layout\n" ); break; case 's': // is a string { char l10n[MAX_STRING_CHARS]; Q_strncpyz( string, CG_TranslateColoredString( token, l10n, sizeof( l10n ) ), sizeof( string ) ); } break; case 'n': // is a player name indicated by player number i = atoi( token ); if( i < 0 ) // negative numbers toggle transparency on { trans = true; i = -1 - i; } if( i < 0 || i >= gs.maxclients ) Q_strncpyz( string, "invalid", sizeof( string ) ); else Q_strncpyz( string, cgs.clientInfo[i].name, sizeof( string ) ); if( ISVIEWERENTITY( i + 1 ) ) // highlight if it's our own player highlight = true; break; case 'i': // is a integer (negatives are colored in red) i = atoi( token ); Q_snprintfz( string, sizeof( string ), "%i", i ); VectorCopy( i >= 0 ? colorWhite : colorRed, color ); break; case 'f': // is a float Q_snprintfz( string, sizeof( string ), "%.2f", atof( token ) ); break; case 'l': // p is an integer colored in latency style i = atoi( token ); Q_snprintfz( string, sizeof( string ), "%i", i ); CG_PingColor( i, color ); break; case 'b': // is a Y/N boolean i = atoi( token ); Q_snprintfz( string, sizeof( string ), "%s", CG_TranslateString( ( i != 0 ) ? "Yes" : "No" ) ); VectorCopy( i ? colorGreen : colorRed, color ); break; case 'p': // is a picture. It uses height for width to get a square iconnum = atoi( token ); if( ( iconnum > 0 ) && ( iconnum < MAX_IMAGES ) ) icon = cgs.imagePrecache[iconnum]; break; case 't': // is a race time. Convert time into MM:SS:mm { unsigned int milli, min, sec; milli = (unsigned int)( atoi( token ) ); if( !milli ) Q_snprintfz( string, sizeof( string ), CG_TranslateString( "no time" ) ); else { min = milli / 60000; milli -= min * 60000; sec = milli / 1000; milli -= sec * 1000; Q_snprintfz( string, sizeof( string ), va( "%02i:%02i.%03i", min, sec, milli ) ); } } break; case 'r': // is a ready state tick that is hidden when not in warmup if( atoi( token ) ) icon = CG_MediaShader( cgs.media.shaderVSayIcon[VSAY_YES] ); break; } if( !width ) continue; // draw the column background teamcolor[3] = SCB_BACKGROUND_ALPHA; if( columncount & 1 ) teamcolor[3] -= 0.15; if( highlight ) teamcolor[3] += 0.3; if( trans ) color[3] = 0.3; if( !pass ) { trap_R_DrawStretchPic( x + xoffset, y + yoffset, width, height, 0, 0, 1, 1, teamcolor, cgs.shaderWhite ); if( icon ) SCR_AddPlayerIcon( icon, x + xoffset, y + yoffset, color[3], font ); } // draw the column value if( pass && string[0] ) { trap_SCR_DrawClampString( x + xoffset, y + yoffset, string, x + xoffset, y + yoffset, x + xoffset + width, y + yoffset + height, font, color ); } columncount++; xoffset += width; } yoffset += height; return yoffset; }
/* * CG_BulletExplosion */ void CG_BulletExplosion( vec3_t pos, vec_t *dir, trace_t *trace ) { lentity_t *le; vec3_t angles; vec3_t local_dir, end; trace_t local_trace, *tr; assert( dir || trace ); if( dir ) { // find what are we hitting tr = &local_trace; VectorMA( pos, -1.0, dir, end ); CG_Trace( tr, pos, vec3_origin, vec3_origin, end, cg.view.POVent, MASK_SHOT ); if( tr->fraction == 1.0 ) return; } else { tr = trace; dir = local_dir; VectorCopy( tr->plane.normal, dir ); } VecToAngles( dir, angles ); if( tr->surfFlags & SURF_FLESH || ( tr->ent > 0 && cg_entities[tr->ent].current.type == ET_PLAYER ) || ( tr->ent > 0 && cg_entities[tr->ent].current.type == ET_CORPSE ) ) { le = CG_AllocModel( LE_ALPHA_FADE, pos, angles, 3, //3 frames for weak 1, 0, 0, 1, //full white no inducted alpha 0, 0, 0, 0, //dlight CG_MediaModel( cgs.media.modBulletExplode ), NULL ); le->ent.rotation = rand() % 360; le->ent.scale = 1.0f; if( ISVIEWERENTITY( tr->ent ) ) le->ent.renderfx |= RF_VIEWERMODEL; } else if( tr->surfFlags & SURF_DUST ) { // throw particles on dust CG_ImpactSmokePuff( tr->endpos, tr->plane.normal, 4, 0.6f, 6, 8 ); } else { le = CG_AllocModel( LE_ALPHA_FADE, pos, angles, 3, //3 frames for weak 1, 1, 1, 1, //full white no inducted alpha 0, 0, 0, 0, //dlight CG_MediaModel( cgs.media.modBulletExplode ), NULL ); le->ent.rotation = rand() % 360; le->ent.scale = 1.0f; CG_ImpactSmokePuff( tr->endpos, tr->plane.normal, 2, 0.6f, 6, 8 ); if( !( tr->surfFlags & SURF_NOMARKS ) ) CG_SpawnDecal( pos, dir, random()*360, 8, 1, 1, 1, 1, 10, 1, qfalse, CG_MediaShader( cgs.media.shaderBulletMark ) ); } }
/* * CG_Event_Fall */ void CG_Event_Fall( entity_state_t *state, int parm ) { if( ISVIEWERENTITY( state->number ) ) { if( cg.frame.playerState.pmove.pm_type != PM_NORMAL ) { CG_SexedSound( state->number, CHAN_AUTO, "*fall_0", cg_volume_players->value ); return; } CG_ViewWeapon_StartFallKickEff( parm ); if( parm > 0 ) CG_DamageIndicatorAdd( parm, tv( 0, 0, 1 ) ); } if( parm > 10 ) { CG_SexedSound( state->number, CHAN_PAIN, "*fall_2", cg_volume_players->value ); switch( (int)brandom( 0, 3 ) ) { case 0: CG_PModel_AddAnimation( state->number, 0, TORSO_PAIN1, 0, EVENT_CHANNEL ); break; case 1: CG_PModel_AddAnimation( state->number, 0, TORSO_PAIN2, 0, EVENT_CHANNEL ); break; case 2: default: CG_PModel_AddAnimation( state->number, 0, TORSO_PAIN3, 0, EVENT_CHANNEL ); break; } } else if( parm > 0 ) { CG_SexedSound( state->number, CHAN_PAIN, "*fall_1", cg_volume_players->value ); } else CG_SexedSound( state->number, CHAN_PAIN, "*fall_0", cg_volume_players->value ); // smoke effect if( parm > 0 && ( cg_cartoonEffects->integer & 2 ) ) { vec3_t start, end; trace_t trace; if( ISVIEWERENTITY( state->number ) ) VectorCopy( cg.predictedPlayerState.pmove.origin, start ); else VectorCopy( state->origin, start ); VectorCopy( start, end ); end[2] += playerbox_stand_mins[2] - 48.0f; CG_Trace( &trace, start, vec3_origin, vec3_origin, end, state->number, MASK_PLAYERSOLID ); if( trace.ent == -1 ) { start[2] += playerbox_stand_mins[2] + 8; CG_DustCircle( start, tv( 0, 0, 1 ), 50, 12 ); } else if( !(trace.surfFlags & SURF_NODAMAGE) ) { VectorMA( trace.endpos, 8, trace.plane.normal, end ); CG_DustCircle( end, trace.plane.normal, 50, 12 ); } } }
/* * CG_FireWeaponEvent */ static void CG_FireWeaponEvent( int entNum, int weapon, int fireMode ) { float attenuation; struct sfx_s *sound = NULL; weaponinfo_t *weaponInfo; if( !weapon ) return; // hack idle attenuation on the plasmagun to reduce sound flood on the scene if( weapon == WEAP_PLASMAGUN ) attenuation = ATTN_IDLE; else attenuation = ATTN_NORM; weaponInfo = CG_GetWeaponInfo( weapon ); // sound if( fireMode == FIRE_MODE_STRONG ) { if( weaponInfo->num_strongfire_sounds ) sound = weaponInfo->sound_strongfire[(int)brandom( 0, weaponInfo->num_strongfire_sounds )]; } else { if( weaponInfo->num_fire_sounds ) sound = weaponInfo->sound_fire[(int)brandom( 0, weaponInfo->num_fire_sounds )]; } if( sound ) { if( ISVIEWERENTITY( entNum ) ) trap_S_StartGlobalSound( sound, CHAN_MUZZLEFLASH, cg_volume_effects->value ); else // fixed position is better for location, but the channels are used from worldspawn // and openal runs out of channels quick on cheap cards. Relative sound uses per-entity channels. trap_S_StartRelativeSound( sound, entNum, CHAN_MUZZLEFLASH, cg_volume_effects->value, attenuation ); if( ( cg_entities[entNum].current.effects & EF_QUAD ) && ( weapon != WEAP_LASERGUN ) ) { struct sfx_s *quadSfx = CG_MediaSfx( cgs.media.sfxQuadFireSound ); if( ISVIEWERENTITY( entNum ) ) trap_S_StartGlobalSound( quadSfx, CHAN_AUTO, cg_volume_effects->value ); else trap_S_StartRelativeSound( quadSfx, entNum, CHAN_AUTO, cg_volume_effects->value, attenuation ); } } // flash and barrel effects if( weapon == WEAP_GUNBLADE ) // gunblade is special { if( fireMode == FIRE_MODE_STRONG ) { // light flash if( cg_weaponFlashes->integer && weaponInfo->flashTime ) cg_entPModels[entNum].flash_time = cg.time + weaponInfo->flashTime; } else { // start barrel rotation or offsetting if( weaponInfo->barrelTime ) cg_entPModels[entNum].barrel_time = cg.time + weaponInfo->barrelTime; } } else { // light flash if( cg_weaponFlashes->integer && weaponInfo->flashTime ) cg_entPModels[entNum].flash_time = cg.time + weaponInfo->flashTime; // start barrel rotation or offsetting if( weaponInfo->barrelTime ) cg_entPModels[entNum].barrel_time = cg.time + weaponInfo->barrelTime; } // add animation to the player model switch( weapon ) { case WEAP_NONE: break; case WEAP_GUNBLADE: if( fireMode == FIRE_MODE_WEAK ) CG_PModel_AddAnimation( entNum, 0, TORSO_SHOOT_BLADE, 0, EVENT_CHANNEL ); else CG_PModel_AddAnimation( entNum, 0, TORSO_SHOOT_PISTOL, 0, EVENT_CHANNEL ); break; case WEAP_LASERGUN: CG_PModel_AddAnimation( entNum, 0, TORSO_SHOOT_PISTOL, 0, EVENT_CHANNEL ); break; default: case WEAP_RIOTGUN: case WEAP_PLASMAGUN: CG_PModel_AddAnimation( entNum, 0, TORSO_SHOOT_LIGHTWEAPON, 0, EVENT_CHANNEL ); break; case WEAP_ROCKETLAUNCHER: case WEAP_GRENADELAUNCHER: CG_PModel_AddAnimation( entNum, 0, TORSO_SHOOT_HEAVYWEAPON, 0, EVENT_CHANNEL ); break; case WEAP_ELECTROBOLT: CG_PModel_AddAnimation( entNum, 0, TORSO_SHOOT_AIMWEAPON, 0, EVENT_CHANNEL ); break; } // add animation to the view weapon model if( ISVIEWERENTITY( entNum ) && !cg.view.thirdperson ) CG_ViewWeapon_StartAnimationEvent( fireMode == FIRE_MODE_STRONG ? WEAPMODEL_ATTACK_STRONG : WEAPMODEL_ATTACK_WEAK ); }
void CG_LaserBeamEffect( centity_t *cent ) { struct sfx_s *sound = NULL; float range; trace_t trace; orientation_t projectsource; vec4_t color; vec3_t laserOrigin, laserAngles, laserPoint; int i, j; if( cent->localEffects[LOCALEFFECT_LASERBEAM] <= cg.time ) return; laserOwner = cent; if( cg_teamColoredBeams->integer && ( cent->current.team == TEAM_ALPHA || cent->current.team == TEAM_BETA ) ) CG_TeamColor( cent->current.team, color ); else Vector4Set( color, 1, 1, 1, 1 ); // interpolate the positions if( ISVIEWERENTITY( cent->current.number ) && !cg.view.thirdperson ) { VectorCopy( cg.predictedPlayerState.pmove.origin, laserOrigin ); laserOrigin[2] += cg.predictedPlayerState.viewheight; VectorCopy( cg.predictedPlayerState.viewangles, laserAngles ); VectorLerp( cent->laserPointOld, cg.lerpfrac, cent->laserPoint, laserPoint ); } else { VectorLerp( cent->laserOriginOld, cg.lerpfrac, cent->laserOrigin, laserOrigin ); VectorLerp( cent->laserPointOld, cg.lerpfrac, cent->laserPoint, laserPoint ); if( !cent->laserCurved ) { vec3_t dir; // make up the angles from the start and end points (s->angles is not so precise) VectorSubtract( laserPoint, laserOrigin, dir ); VecToAngles( dir, laserAngles ); } else // use player entity angles { for( i = 0; i < 3; i++ ) laserAngles[i] = LerpAngle( cent->prev.angles[i], cent->current.angles[i], cg.lerpfrac ); } } if( !cent->laserCurved ) { range = GS_GetWeaponDef( WEAP_LASERGUN )->firedef.timeout; if( cent->current.effects & EF_QUAD ) sound = CG_MediaSfx( cgs.media.sfxLasergunStrongQuadHum ); else sound = CG_MediaSfx( cgs.media.sfxLasergunStrongHum ); // trace the beam: for tracing we use the real beam origin GS_TraceLaserBeam( &trace, laserOrigin, laserAngles, range, cent->current.number, 0, _LaserImpact ); // draw the beam: for drawing we use the weapon projection source (already handles the case of viewer entity) if( !CG_PModel_GetProjectionSource( cent->current.number, &projectsource ) ) VectorCopy( laserOrigin, projectsource.origin ); CG_KillPolyBeamsByTag( cent->current.number ); CG_LaserGunPolyBeam( projectsource.origin, trace.endpos, color, cent->current.number ); } else { float frac, subdivisions = cg_laserBeamSubdivisions->integer; vec3_t from, dir, end, blendPoint; int passthrough = cent->current.number; vec3_t tmpangles, blendAngles; range = GS_GetWeaponDef( WEAP_LASERGUN )->firedef_weak.timeout; if( cent->current.effects & EF_QUAD ) sound = CG_MediaSfx( cgs.media.sfxLasergunWeakQuadHum ); else sound = CG_MediaSfx( cgs.media.sfxLasergunWeakHum ); // trace the beam: for tracing we use the real beam origin GS_TraceCurveLaserBeam( &trace, laserOrigin, laserAngles, laserPoint, cent->current.number, 0, _LaserImpact ); // draw the beam: for drawing we use the weapon projection source (already handles the case of viewer entity) if( !CG_PModel_GetProjectionSource( cent->current.number, &projectsource ) ) VectorCopy( laserOrigin, projectsource.origin ); if( subdivisions < CURVELASERBEAM_SUBDIVISIONS ) subdivisions = CURVELASERBEAM_SUBDIVISIONS; CG_KillPolyBeamsByTag( cent->current.number ); // we redraw the full beam again, and trace each segment for stop dead impact VectorCopy( laserPoint, blendPoint ); VectorCopy( projectsource.origin, from ); VectorSubtract( blendPoint, projectsource.origin, dir ); VecToAngles( dir, blendAngles ); for( i = 1; i <= (int)subdivisions; i++ ) { frac = ( ( range/subdivisions )*(float)i ) / (float)range; for( j = 0; j < 3; j++ ) tmpangles[j] = LerpAngle( laserAngles[j], blendAngles[j], frac ); AngleVectors( tmpangles, dir, NULL, NULL ); VectorMA( projectsource.origin, range * frac, dir, end ); GS_TraceLaserBeam( &trace, from, tmpangles, DistanceFast( from, end ), passthrough, 0, NULL ); CG_LaserGunPolyBeam( from, trace.endpos, color, cent->current.number ); if( trace.fraction != 1.0f ) break; passthrough = trace.ent; VectorCopy( trace.endpos, from ); } } // enable continuous flash on the weapon owner if( cg_weaponFlashes->integer ) cg_entPModels[cent->current.number].flash_time = cg.time + CG_GetWeaponInfo( WEAP_LASERGUN )->flashTime; if( sound ) { if( ISVIEWERENTITY( cent->current.number ) ) trap_S_AddLoopSound( sound, cent->current.number, 1.0, ATTN_NONE ); else trap_S_AddLoopSound( sound, cent->current.number, 1.0, ATTN_STATIC ); } laserOwner = NULL; }
/* * CG_EntityEvent */ void CG_EntityEvent( entity_state_t *ent, int ev, int parm, qboolean predicted ) { //static orientation_t projection; vec3_t dir; qboolean viewer = ISVIEWERENTITY( ent->number ); vec4_t color; int weapon = 0, fireMode = 0, count = 0; if( viewer && ( ev < PREDICTABLE_EVENTS_MAX ) && ( predicted != cg.view.playerPrediction ) ) return; switch( ev ) { case EV_NONE: default: break; // PREDICTABLE EVENTS case EV_WEAPONACTIVATE: CG_PModel_AddAnimation( ent->number, 0, TORSO_WEAPON_SWITCHIN, 0, EVENT_CHANNEL ); weapon = ( parm & ~EV_INVERSE ); fireMode = ( parm & EV_INVERSE ) ? FIRE_MODE_STRONG : FIRE_MODE_WEAK; if( predicted ) { cg_entities[ent->number].current.weapon = weapon; if( fireMode == FIRE_MODE_STRONG ) cg_entities[ent->number].current.effects |= EF_STRONG_WEAPON; CG_ViewWeapon_RefreshAnimation( &cg.weapon ); } if( viewer ) cg.predictedWeaponSwitch = 0; if( viewer ) trap_S_StartGlobalSound( CG_MediaSfx( cgs.media.sfxWeaponUp ), CHAN_AUTO, cg_volume_effects->value ); else trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxWeaponUp ), ent->origin, CHAN_AUTO, cg_volume_effects->value, ATTN_NORM ); break; case EV_SMOOTHREFIREWEAPON: // the server never sends this event if( predicted ) { weapon = ( parm & ~EV_INVERSE ); fireMode = ( parm & EV_INVERSE ) ? FIRE_MODE_STRONG : FIRE_MODE_WEAK; cg_entities[ent->number].current.weapon = weapon; if( fireMode == FIRE_MODE_STRONG ) cg_entities[ent->number].current.effects |= EF_STRONG_WEAPON; CG_ViewWeapon_RefreshAnimation( &cg.weapon ); if( weapon == WEAP_LASERGUN ) CG_Event_LaserBeam( ent->number, weapon, fireMode ); } break; case EV_FIREWEAPON: weapon = ( parm & ~EV_INVERSE ); fireMode = ( parm & EV_INVERSE ) ? FIRE_MODE_STRONG : FIRE_MODE_WEAK; if( predicted ) { cg_entities[ent->number].current.weapon = weapon; if( fireMode == FIRE_MODE_STRONG ) cg_entities[ent->number].current.effects |= EF_STRONG_WEAPON; } CG_FireWeaponEvent( ent->number, weapon, fireMode ); // riotgun bullets, electrobolt and instagun beams are predicted when the weapon is fired if( predicted ) { vec3_t origin; if( ( weapon == WEAP_ELECTROBOLT #ifndef ELECTROBOLT_TEST && fireMode == FIRE_MODE_STRONG #endif ) || weapon == WEAP_INSTAGUN ) { VectorCopy( cg.predictedPlayerState.pmove.origin, origin ); origin[2] += cg.predictedPlayerState.viewheight; AngleVectors( cg.predictedPlayerState.viewangles, dir, NULL, NULL ); CG_Event_WeaponBeam( origin, dir, cg.predictedPlayerState.POVnum, weapon, fireMode ); } else if( weapon == WEAP_RIOTGUN || weapon == WEAP_MACHINEGUN ) { int seed = cg.predictedEventTimes[EV_FIREWEAPON] & 255; VectorCopy( cg.predictedPlayerState.pmove.origin, origin ); origin[2] += cg.predictedPlayerState.viewheight; AngleVectors( cg.predictedPlayerState.viewangles, dir, NULL, NULL ); if( weapon == WEAP_RIOTGUN ) CG_Event_FireRiotgun( origin, dir, weapon, fireMode, seed, cg.predictedPlayerState.POVnum ); else CG_Event_FireMachinegun( origin, dir, weapon, fireMode, seed, cg.predictedPlayerState.POVnum ); } else if( weapon == WEAP_LASERGUN ) { CG_Event_LaserBeam( ent->number, weapon, fireMode ); } } break; case EV_ELECTROTRAIL: // check the owner for predicted case if( ISVIEWERENTITY( parm ) && ( ev < PREDICTABLE_EVENTS_MAX ) && ( predicted != cg.view.playerPrediction ) ) return; CG_Event_WeaponBeam( ent->origin, ent->origin2, parm, WEAP_ELECTROBOLT, ent->firemode ); break; case EV_INSTATRAIL: // check the owner for predicted case if( ISVIEWERENTITY( parm ) && ( ev < PREDICTABLE_EVENTS_MAX ) && ( predicted != cg.view.playerPrediction ) ) return; CG_Event_WeaponBeam( ent->origin, ent->origin2, parm, WEAP_INSTAGUN, FIRE_MODE_STRONG ); break; case EV_FIRE_RIOTGUN: case EV_FIRE_BULLET: { // check the owner for predicted case if( ISVIEWERENTITY( ent->ownerNum ) && ( ev < PREDICTABLE_EVENTS_MAX ) && ( predicted != cg.view.playerPrediction ) ) return; if( ev == EV_FIRE_RIOTGUN ) CG_Event_FireRiotgun( ent->origin, ent->origin2, ( ent->weapon & ~EV_INVERSE ), ( ent->weapon & EV_INVERSE ), parm, ent->ownerNum ); else CG_Event_FireMachinegun( ent->origin, ent->origin2, ( ent->weapon & ~EV_INVERSE ), ( ent->weapon & EV_INVERSE ), parm, ent->ownerNum ); } break; case EV_NOAMMOCLICK: if( viewer ) trap_S_StartGlobalSound( CG_MediaSfx( cgs.media.sfxWeaponUpNoAmmo ), CHAN_ITEM, cg_volume_effects->value ); else trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxWeaponUpNoAmmo ), ent->origin, CHAN_ITEM, cg_volume_effects->value, ATTN_IDLE ); break; case EV_DASH: CG_Event_Dash( ent, parm ); break; case EV_WALLJUMP: case EV_WALLJUMP_FAILED: CG_Event_WallJump( ent, parm, ev ); break; case EV_DOUBLEJUMP: CG_Event_DoubleJump( ent, parm ); break; case EV_JUMP: CG_Event_Jump( ent, parm ); break; case EV_JUMP_PAD: CG_TouchJumpPad( ent->number ); break; case EV_FALL: CG_Event_Fall( ent, parm ); break; // NON PREDICTABLE EVENTS case EV_WEAPONDROP: // deactivate is not predictable CG_PModel_AddAnimation( ent->number, 0, TORSO_WEAPON_SWITCHOUT, 0, EVENT_CHANNEL ); break; case EV_SEXEDSOUND: if( parm == 2 ) CG_SexedSound( ent->number, CHAN_AUTO, S_PLAYER_GASP, cg_volume_players->value ); else if( parm == 1 ) CG_SexedSound( ent->number, CHAN_AUTO, S_PLAYER_DROWN, cg_volume_players->value ); break; case EV_PAIN: CG_Event_Pain( ent, parm ); break; case EV_DIE: CG_Event_Die( ent, parm ); break; case EV_GIB: break; case EV_EXPLOSION1: CG_GenericExplosion( ent->origin, vec3_origin, FIRE_MODE_WEAK, parm * 8 ); break; case EV_EXPLOSION2: CG_GenericExplosion( ent->origin, vec3_origin, FIRE_MODE_STRONG, parm * 16 ); break; case EV_GREEN_LASER: CG_GreenLaser( ent->origin, ent->origin2 ); break; case EV_PNODE: color[0] = COLOR_R( ent->colorRGBA ) * ( 1.0 / 255.0 ); color[1] = COLOR_G( ent->colorRGBA ) * ( 1.0 / 255.0 ); color[2] = COLOR_B( ent->colorRGBA ) * ( 1.0 / 255.0 ); color[3] = COLOR_A( ent->colorRGBA ) * ( 1.0 / 255.0 ); CG_PLink( ent->origin, ent->origin2, color, parm ); break; case EV_SPARKS: ByteToDir( parm, dir ); if( ent->damage > 0 ) { count = (int)( ent->damage * 0.25f ); clamp( count, 1, 10 ); } else count = 6; CG_ParticleEffect( ent->origin, dir, 1.0f, 0.67f, 0.0f, count ); break; case EV_BULLET_SPARKS: ByteToDir( parm, dir ); CG_BulletExplosion( ent->origin, dir, NULL ); CG_ParticleEffect( ent->origin, dir, 1.0f, 0.67f, 0.0f, 6 ); trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxRic[rand()&2] ), ent->origin, CHAN_AUTO, cg_volume_effects->value, ATTN_STATIC ); break; case EV_LASER_SPARKS: ByteToDir( parm, dir ); CG_ParticleEffect2( ent->origin, dir, COLOR_R( ent->colorRGBA ) * ( 1.0 / 255.0 ), COLOR_G( ent->colorRGBA ) * ( 1.0 / 255.0 ), COLOR_B( ent->colorRGBA ) * ( 1.0 / 255.0 ), ent->eventCount ); break; case EV_GESTURE: CG_SexedSound( ent->number, CHAN_BODY, "*taunt", cg_volume_players->value ); break; case EV_DROP: CG_PModel_AddAnimation( ent->number, 0, TORSO_DROP, 0, EVENT_CHANNEL ); break; case EV_SPOG: CG_SmallPileOfGibs( ent->origin, parm, ent->origin2 ); break; case EV_ITEM_RESPAWN: cg_entities[ent->number].respawnTime = cg.time; trap_S_StartRelativeSound( CG_MediaSfx( cgs.media.sfxItemRespawn ), ent->number, CHAN_AUTO, cg_volume_effects->value, ATTN_IDLE ); break; case EV_PLAYER_RESPAWN: if( (unsigned)ent->ownerNum == cgs.playerNum + 1 ) { CG_ResetKickAngles(); CG_ResetColorBlend(); CG_ResetDamageIndicator(); } // fallthrough case EV_PLAYER_TELEPORT_IN: if( ISVIEWERENTITY( ent->ownerNum ) ) { trap_S_StartGlobalSound( CG_MediaSfx( cgs.media.sfxTeleportIn ), CHAN_AUTO, cg_volume_effects->value ); } else { trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxTeleportIn ), ent->origin, CHAN_AUTO, cg_volume_effects->value, ATTN_NORM ); } if( ent->ownerNum && ent->ownerNum < gs.maxclients + 1 ) cg_entities[ent->ownerNum].localEffects[LOCALEFFECT_EV_PLAYER_TELEPORT_IN] = cg.time; break; case EV_PLAYER_TELEPORT_OUT: if( ISVIEWERENTITY( ent->ownerNum ) ) { trap_S_StartGlobalSound( CG_MediaSfx( cgs.media.sfxTeleportOut ), CHAN_AUTO, cg_volume_effects->value ); } else { trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxTeleportOut ), ent->origin, CHAN_AUTO, cg_volume_effects->value, ATTN_NORM ); } if( ent->ownerNum && ent->ownerNum < gs.maxclients + 1 ) { cg_entities[ent->ownerNum].localEffects[LOCALEFFECT_EV_PLAYER_TELEPORT_OUT] = cg.time; VectorCopy( ent->origin, cg_entities[ent->ownerNum].teleportedFrom ); } break; case EV_PLASMA_EXPLOSION: ByteToDir( parm, dir ); CG_PlasmaExplosion( ent->origin, dir, ent->firemode, (float)ent->weapon * 8.0f ); if( ent->firemode == FIRE_MODE_STRONG ) { trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxPlasmaStrongHit ), ent->origin, CHAN_AUTO, cg_volume_effects->value, ATTN_IDLE ); CG_StartKickAnglesEffect( ent->origin, 50, ent->weapon * 8, 100 ); } else { trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxPlasmaWeakHit ), ent->origin, CHAN_AUTO, cg_volume_effects->value, ATTN_IDLE ); CG_StartKickAnglesEffect( ent->origin, 30, ent->weapon * 8, 75 ); } break; case EV_BOLT_EXPLOSION: ByteToDir( parm, dir ); CG_BoltExplosionMode( ent->origin, dir, ent->firemode ); break; case EV_INSTA_EXPLOSION: ByteToDir( parm, dir ); CG_InstaExplosionMode( ent->origin, dir, ent->firemode ); break; case EV_GRENADE_EXPLOSION: if( parm ) { // we have a direction ByteToDir( parm, dir ); CG_GrenadeExplosionMode( ent->origin, dir, ent->firemode, (float)ent->weapon*8.0f ); } else { // no direction CG_GrenadeExplosionMode( ent->origin, vec3_origin, ent->firemode, (float)ent->weapon*8.0f ); } if( ent->firemode == FIRE_MODE_STRONG ) CG_StartKickAnglesEffect( ent->origin, 135, ent->weapon*8, 325 ); else CG_StartKickAnglesEffect( ent->origin, 125, ent->weapon*8, 300 ); break; case EV_ROCKET_EXPLOSION: ByteToDir( parm, dir ); CG_RocketExplosionMode( ent->origin, dir, ent->firemode, (float)ent->weapon * 8.0f ); if( ent->firemode == FIRE_MODE_STRONG ) CG_StartKickAnglesEffect( ent->origin, 135, ent->weapon * 8, 300 ); else CG_StartKickAnglesEffect( ent->origin, 125, ent->weapon * 8, 275 ); break; case EV_GRENADE_BOUNCE: if( parm == FIRE_MODE_STRONG ) trap_S_StartRelativeSound( CG_MediaSfx( cgs.media.sfxGrenadeStrongBounce[rand()&1] ), ent->number, CHAN_AUTO, cg_volume_effects->value, ATTN_IDLE ); else trap_S_StartRelativeSound( CG_MediaSfx( cgs.media.sfxGrenadeWeakBounce[rand()&1] ), ent->number, CHAN_AUTO, cg_volume_effects->value, ATTN_IDLE ); break; case EV_BLADE_IMPACT: CG_BladeImpact( ent->origin, ent->origin2 ); break; case EV_GUNBLADEBLAST_IMPACT: ByteToDir( parm, dir ); CG_GunBladeBlastImpact( ent->origin, dir, (float)ent->weapon*8 ); if( ent->skinnum > 64 ) { trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxGunbladeStrongHit[2] ), ent->origin, CHAN_AUTO, cg_volume_effects->value, ATTN_DISTANT ); } else if( ent->skinnum > 34 ) { trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxGunbladeStrongHit[1] ), ent->origin, CHAN_AUTO, cg_volume_effects->value, ATTN_NORM ); } else { trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxGunbladeStrongHit[0] ), ent->origin, CHAN_AUTO, cg_volume_effects->value, ATTN_IDLE ); } //ent->skinnum is knockback value CG_StartKickAnglesEffect( ent->origin, ent->skinnum*8, ent->weapon*8, 200 ); break; case EV_BLOOD: if( cg_showBloodTrail->integer == 2 && ISVIEWERENTITY( ent->ownerNum ) ) break; ByteToDir( parm, dir ); CG_BloodDamageEffect( ent->origin, dir, ent->damage ); CG_CartoonHitEffect( ent->origin, dir, ent->damage ); break; // func movers case EV_PLAT_HIT_TOP: case EV_PLAT_HIT_BOTTOM: case EV_PLAT_START_MOVING: case EV_DOOR_HIT_TOP: case EV_DOOR_HIT_BOTTOM: case EV_DOOR_START_MOVING: case EV_BUTTON_FIRE: case EV_TRAIN_STOP: case EV_TRAIN_START: { vec3_t so; CG_GetEntitySpatilization( ent->number, so, NULL ); trap_S_StartFixedSound( cgs.soundPrecache[parm], so, CHAN_AUTO, cg_volume_effects->value, ATTN_STATIC ); } break; case EV_VSAY: CG_StartVoiceTokenEffect( ent->ownerNum, EV_VSAY, parm ); break; } }