// Copyright (C) 1999-2000 Id Software, Inc. // // cg_event.c -- handle entity events at snapshot or playerstate transitions #include "cg_local.h" #include "fx_local.h" #include "../ui/ui_shared.h" #include "../ui/ui_public.h" // for the voice chats #include "../../ui/menudef.h" #include "../ghoul2/G2.h" //========================================================================== extern qboolean WP_SaberBladeUseSecondBladeStyle( saberInfo_t *saber, int bladeNum ); extern qboolean CG_VehicleWeaponImpact( centity_t *cent ); extern qboolean CG_InFighter( void ); extern qboolean CG_InATST( void ); extern int cg_saberFlashTime; extern vec3_t cg_saberFlashPos; extern char *showPowersName[]; extern int cg_siegeDeathTime; extern int cg_siegeDeathDelay; extern int cg_vehicleAmmoWarning; extern int cg_vehicleAmmoWarningTime; //I know, not siege, but... typedef enum { TAUNT_TAUNT = 0, TAUNT_BOW, TAUNT_MEDITATE, TAUNT_FLOURISH, TAUNT_GLOAT }; /* =================== CG_PlaceString Also called by scoreboard drawing =================== */ const char *CG_PlaceString( int rank ) { static char str[64]; char *s, *t; // number extenstions, eg 1st, 2nd, 3rd, 4th etc. // note that the rules are different for french, but by changing the required strip strings they seem to work char sST[10]; char sND[10]; char sRD[10]; char sTH[10]; char sTiedFor[64]; // german is much longer, super safe... trap_SP_GetStringTextString("MP_INGAME_NUMBER_ST",sST, sizeof(sST) ); trap_SP_GetStringTextString("MP_INGAME_NUMBER_ND",sND, sizeof(sND) ); trap_SP_GetStringTextString("MP_INGAME_NUMBER_RD",sRD, sizeof(sRD) ); trap_SP_GetStringTextString("MP_INGAME_NUMBER_TH",sTH, sizeof(sTH) ); trap_SP_GetStringTextString("MP_INGAME_TIED_FOR" ,sTiedFor,sizeof(sTiedFor) ); strcat(sTiedFor," "); // save worrying about translators adding spaces or not if ( rank & RANK_TIED_FLAG ) { rank &= ~RANK_TIED_FLAG; t = sTiedFor;//"Tied for "; } else { t = ""; } if ( rank == 1 ) { s = va("1%s",sST);//S_COLOR_BLUE "1st" S_COLOR_WHITE; // draw in blue } else if ( rank == 2 ) { s = va("2%s",sND);//S_COLOR_RED "2nd" S_COLOR_WHITE; // draw in red } else if ( rank == 3 ) { s = va("3%s",sRD);//S_COLOR_YELLOW "3rd" S_COLOR_WHITE; // draw in yellow } else if ( rank == 11 ) { s = va("11%s",sTH); } else if ( rank == 12 ) { s = va("12%s",sTH); } else if ( rank == 13 ) { s = va("13%s",sTH); } else if ( rank % 10 == 1 ) { s = va("%i%s", rank,sST); } else if ( rank % 10 == 2 ) { s = va("%i%s", rank,sND); } else if ( rank % 10 == 3 ) { s = va("%i%s", rank,sRD); } else { s = va("%i%s", rank,sTH); } Com_sprintf( str, sizeof( str ), "%s%s", t, s ); return str; } qboolean CG_ThereIsAMaster(void); /* ============= CG_Obituary ============= */ static void CG_Obituary( entityState_t *ent ) { int mod; int target, attacker; char *message; const char *targetInfo; const char *attackerInfo; char targetName[32]; char targetVehName[32] = {0}; char attackerName[32]; char attackerVehName[32] = {0}; char attackerVehWeapName[32] = {0}; gender_t gender; clientInfo_t *ci; qboolean vehMessage = qfalse; target = ent->otherEntityNum; attacker = ent->otherEntityNum2; mod = ent->eventParm; if ( target < 0 || target >= MAX_CLIENTS ) { CG_Error( "CG_Obituary: target out of range" ); } ci = &cgs.clientinfo[target]; if ( attacker < 0 || attacker >= MAX_CLIENTS ) { //attacker = ENTITYNUM_WORLD; attackerInfo = NULL; } else { attackerInfo = CG_ConfigString( CS_PLAYERS + attacker ); } targetInfo = CG_ConfigString( CS_PLAYERS + target ); if ( !targetInfo ) { return; } Q_strncpyz( targetName, Info_ValueForKey( targetInfo, "n" ), sizeof(targetName) - 2); strcat( targetName, S_COLOR_WHITE ); // check for target in a vehicle if ( ent->lookTarget > VEHICLE_BASE && ent->lookTarget < MAX_VEHICLES && g_vehicleInfo[ent->lookTarget].name ) { Q_strncpyz( targetVehName, g_vehicleInfo[ent->lookTarget].name, sizeof(targetVehName) - 2 ); } // check for attacker in a vehicle if ( ent->brokenLimbs >= MAX_CLIENTS ) { centity_t *attVehCent = &cg_entities[ent->brokenLimbs]; if ( attVehCent && attVehCent->m_pVehicle && attVehCent->m_pVehicle->m_pVehicleInfo ) { if ( attVehCent->m_pVehicle->m_pVehicleInfo->name ) { Q_strncpyz( attackerVehName, attVehCent->m_pVehicle->m_pVehicleInfo->name, sizeof(attackerVehName) - 2 ); } } } //check for specific vehicle weapon if ( ent->weapon > 0 ) { if ( g_vehWeaponInfo[ent->weapon-1].name ) { Q_strncpyz( attackerVehWeapName, g_vehWeaponInfo[ent->weapon-1].name, sizeof(attackerVehWeapName) - 2 ); } } // check for single client messages if ( ent->saberInFlight ) {//asteroid->vehicle collision switch ( Q_irand( 0, 2 ) ) { default: case 0: message = "DIED_ASTEROID1"; break; case 1: message = "DIED_ASTEROID2"; break; case 2: message = "DIED_ASTEROID3"; break; } vehMessage = qtrue; } else { switch( mod ) { case MOD_VEHICLE: case MOD_SUICIDE: case MOD_FALLING: case MOD_COLLISION: case MOD_VEH_EXPLOSION: case MOD_CRUSH: case MOD_WATER: case MOD_SLIME: case MOD_LAVA: case MOD_TRIGGER_HURT: message = "DIED_GENERIC"; break; case MOD_TARGET_LASER: vehMessage = qtrue; message = "DIED_TURBOLASER"; break; default: message = NULL; break; } } // Attacker killed themselves. Ridicule them for it. if (attacker == target) { vehMessage = qfalse; gender = ci->gender; switch (mod) { case MOD_BRYAR_PISTOL: case MOD_BRYAR_PISTOL_ALT: case MOD_BLASTER: case MOD_TURBLAST: case MOD_DISRUPTOR: case MOD_DISRUPTOR_SPLASH: case MOD_DISRUPTOR_SNIPER: case MOD_BOWCASTER: case MOD_REPEATER: case MOD_REPEATER_ALT: case MOD_FLECHETTE: if ( gender == GENDER_FEMALE ) message = "SUICIDE_SHOT_FEMALE"; else if ( gender == GENDER_NEUTER ) message = "SUICIDE_SHOT_GENDERLESS"; else message = "SUICIDE_SHOT_MALE"; break; case MOD_REPEATER_ALT_SPLASH: case MOD_FLECHETTE_ALT_SPLASH: case MOD_ROCKET: case MOD_ROCKET_SPLASH: case MOD_ROCKET_HOMING: case MOD_ROCKET_HOMING_SPLASH: case MOD_THERMAL: case MOD_THERMAL_SPLASH: case MOD_TRIP_MINE_SPLASH: case MOD_TIMED_MINE_SPLASH: case MOD_DET_PACK_SPLASH: case MOD_VEHICLE: case MOD_CONC: case MOD_CONC_ALT: if ( gender == GENDER_FEMALE ) message = "SUICIDE_EXPLOSIVES_FEMALE"; else if ( gender == GENDER_NEUTER ) message = "SUICIDE_EXPLOSIVES_GENDERLESS"; else message = "SUICIDE_EXPLOSIVES_MALE"; break; case MOD_DEMP2: if ( gender == GENDER_FEMALE ) message = "SUICIDE_ELECTROCUTED_FEMALE"; else if ( gender == GENDER_NEUTER ) message = "SUICIDE_ELECTROCUTED_GENDERLESS"; else message = "SUICIDE_ELECTROCUTED_MALE"; break; case MOD_FALLING: if ( gender == GENDER_FEMALE ) message = "SUICIDE_FALLDEATH_FEMALE"; else if ( gender == GENDER_NEUTER ) message = "SUICIDE_FALLDEATH_GENDERLESS"; else message = "SUICIDE_FALLDEATH_MALE"; break; default: if ( gender == GENDER_FEMALE ) message = "SUICIDE_GENERICDEATH_FEMALE"; else if ( gender == GENDER_NEUTER ) message = "SUICIDE_GENERICDEATH_GENDERLESS"; else message = "SUICIDE_GENERICDEATH_MALE"; break; } } if (target != attacker && target < MAX_CLIENTS && attacker < MAX_CLIENTS) { goto clientkilled; } if (message) { gender = ci->gender; if (!message[0]) { vehMessage = qfalse; if ( gender == GENDER_FEMALE ) message = "SUICIDE_GENERICDEATH_FEMALE"; else if ( gender == GENDER_NEUTER ) message = "SUICIDE_GENERICDEATH_GENDERLESS"; else message = "SUICIDE_GENERICDEATH_MALE"; } if ( vehMessage ) { message = (char *)CG_GetStringEdString("MP_INGAMEVEH", message); } else { message = (char *)CG_GetStringEdString("MP_INGAME", message); } CG_Printf( "%s %s\n", targetName, message); return; } clientkilled: // check for kill messages from the current clientNum if ( attacker == cg.snap->ps.clientNum ) { char *s; if ( cgs.gametype < GT_TEAM && cgs.gametype != GT_DUEL && cgs.gametype != GT_POWERDUEL ) { if (cgs.gametype == GT_JEDIMASTER && attacker < MAX_CLIENTS && !ent->isJediMaster && !cg.snap->ps.isJediMaster && CG_ThereIsAMaster()) { char part1[512]; char part2[512]; trap_SP_GetStringTextString("MP_INGAME_KILLED_MESSAGE", part1, sizeof(part1)); trap_SP_GetStringTextString("MP_INGAME_JMKILLED_NOTJM", part2, sizeof(part2)); s = va("%s %s\n%s\n", part1, targetName, part2); } else if (cgs.gametype == GT_JEDIMASTER && attacker < MAX_CLIENTS && !ent->isJediMaster && !cg.snap->ps.isJediMaster) { //no JM, saber must be out char part1[512]; trap_SP_GetStringTextString("MP_INGAME_KILLED_MESSAGE", part1, sizeof(part1)); /* kmsg1 = "for 0 points.\nGo for the saber!"; strcpy(part2, kmsg1); s = va("%s %s %s\n", part1, targetName, part2); */ s = va("%s %s\n", part1, targetName); } else if (cgs.gametype == GT_POWERDUEL) { s = ""; } else { char sPlaceWith[256]; char sKilledStr[256]; trap_SP_GetStringTextString("MP_INGAME_PLACE_WITH", sPlaceWith, sizeof(sPlaceWith)); trap_SP_GetStringTextString("MP_INGAME_KILLED_MESSAGE", sKilledStr, sizeof(sKilledStr)); s = va("%s %s.\n%s %s %i.", sKilledStr, targetName, CG_PlaceString( cg.snap->ps.persistant[PERS_RANK] + 1 ), sPlaceWith, cg.snap->ps.persistant[PERS_SCORE] ); } } else { char sKilledStr[256]; trap_SP_GetStringTextString("MP_INGAME_KILLED_MESSAGE", sKilledStr, sizeof(sKilledStr)); s = va("%s %s", sKilledStr, targetName ); } //if (!(cg_singlePlayerActive.integer && cg_cameraOrbit.integer)) { CG_CenterPrint( s, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); //} // print the text message as well } // check for double client messages if ( !attackerInfo ) { //attacker = ENTITYNUM_WORLD; strcpy( attackerName, "noname" ); } else { Q_strncpyz( attackerName, Info_ValueForKey( attackerInfo, "n" ), sizeof(attackerName) - 2); strcat( attackerName, S_COLOR_WHITE ); // check for kill messages about the current clientNum if ( target == cg.snap->ps.clientNum ) { Q_strncpyz( cg.killerName, attackerName, sizeof( cg.killerName ) ); } } if ( attacker != ENTITYNUM_WORLD ) { switch (mod) { case MOD_STUN_BATON: message = "KILLED_STUN"; break; case MOD_MELEE: message = "KILLED_MELEE"; break; case MOD_SABER: message = "KILLED_SABER"; break; case MOD_BRYAR_PISTOL: case MOD_BRYAR_PISTOL_ALT: message = "KILLED_BRYAR"; break; case MOD_BLASTER: message = "KILLED_BLASTER"; break; case MOD_TURBLAST: message = "KILLED_BLASTER"; break; case MOD_DISRUPTOR: case MOD_DISRUPTOR_SPLASH: message = "KILLED_DISRUPTOR"; break; case MOD_DISRUPTOR_SNIPER: message = "KILLED_DISRUPTORSNIPE"; break; case MOD_BOWCASTER: message = "KILLED_BOWCASTER"; break; case MOD_REPEATER: message = "KILLED_REPEATER"; break; case MOD_REPEATER_ALT: case MOD_REPEATER_ALT_SPLASH: message = "KILLED_REPEATERALT"; break; case MOD_DEMP2: case MOD_DEMP2_ALT: message = "KILLED_DEMP2"; break; case MOD_FLECHETTE: message = "KILLED_FLECHETTE"; break; case MOD_FLECHETTE_ALT_SPLASH: message = "KILLED_FLECHETTE_MINE"; break; case MOD_ROCKET: case MOD_ROCKET_SPLASH: message = "KILLED_ROCKET"; break; case MOD_ROCKET_HOMING: case MOD_ROCKET_HOMING_SPLASH: message = "KILLED_ROCKET_HOMING"; break; case MOD_THERMAL: case MOD_THERMAL_SPLASH: message = "KILLED_THERMAL"; break; case MOD_TRIP_MINE_SPLASH: message = "KILLED_TRIPMINE"; break; case MOD_TIMED_MINE_SPLASH: message = "KILLED_TRIPMINE_TIMED"; break; case MOD_DET_PACK_SPLASH: message = "KILLED_DETPACK"; break; case MOD_VEHICLE: vehMessage = qtrue; switch ( ent->generic1 ) { case WP_BLASTER://primary blasters switch ( Q_irand( 0, 2 ) ) { case 2: message = "KILLED_VEH_BLASTER3"; break; case 1: message = "KILLED_VEH_BLASTER2"; break; default: message = "KILLED_VEH_BLASTER1"; break; } break; case WP_ROCKET_LAUNCHER://missile if ( Q_irand( 0, 1 ) ) { message = "KILLED_VEH_MISSILE2"; } else { message = "KILLED_VEH_MISSILE1"; } break; case WP_THERMAL://bomb message = "KILLED_VEH_BOMB"; break; case WP_DEMP2://ion cannon message = "KILLED_VEH_ION"; break; case WP_TURRET://turret message = "KILLED_VEH_TURRET"; break; default: vehMessage = qfalse; message = "KILLED_GENERIC"; break; } break; case MOD_CONC: case MOD_CONC_ALT: message = "KILLED_GENERIC"; break; case MOD_FORCE_DARK: message = "KILLED_DARKFORCE"; break; case MOD_SENTRY: message = "KILLED_SENTRY"; break; case MOD_TELEFRAG: message = "KILLED_TELEFRAG"; break; case MOD_CRUSH: message = "KILLED_GENERIC";//"KILLED_FORCETOSS"; break; case MOD_FALLING: message = "KILLED_FORCETOSS"; break; case MOD_COLLISION: case MOD_VEH_EXPLOSION: switch ( Q_irand( 0, 2 ) ) { default: case 0: message = "KILLED_VEH_COLLISION1"; break; case 1: message = "KILLED_VEH_COLLISION2"; break; case 2: message = "KILLED_VEH_COLLISION3"; break; } vehMessage = qtrue; break; case MOD_TRIGGER_HURT: message = "KILLED_GENERIC";//"KILLED_FORCETOSS"; break; case MOD_TARGET_LASER: if ( Q_irand(0,1) ) { message = "KILLED_TURRET1"; } else { message = "KILLED_TURRET2"; } vehMessage = qtrue; break; default: message = "KILLED_GENERIC"; break; } if (message) { if ( vehMessage ) { message = (char *)CG_GetStringEdString("MP_INGAMEVEH", message); } else { message = (char *)CG_GetStringEdString("MP_INGAME", message); } CG_Printf( "%s ", targetName); if ( targetVehName[0] ) { CG_Printf( "(%s) ", targetVehName); } if ( mod == MOD_TARGET_LASER ) {//no attacker name, just a turbolaser or other kind of turret... CG_Printf( "%s", message); } else { CG_Printf( "%s %s", message, attackerName); if ( attackerVehName[0] && attackerVehWeapName[0] ) { CG_Printf( " (%s %s)", attackerVehName, attackerVehWeapName ); } else { if ( attackerVehName[0] ) { CG_Printf( " (%s)", attackerVehName ); } else if ( attackerVehWeapName[0] ) { CG_Printf(" (%s)", attackerVehWeapName ); } } } CG_Printf( "\n" ); return; } } // we don't know what it was CG_Printf( "%s %s\n", targetName, (char *)CG_GetStringEdString("MP_INGAME", "DIED_GENERIC") ); } //========================================================================== void CG_ToggleBinoculars(centity_t *cent, int forceZoom) { if (cent->currentState.number != cg.snap->ps.clientNum) { return; } if (cg.snap->ps.weaponstate != WEAPON_READY) { //So we can't fool it and reactivate while switching to the saber or something. return; } /* if (cg.snap->ps.weapon == WP_SABER) { //No. return; } */ if (forceZoom) { if (forceZoom == 2) { cg.snap->ps.zoomMode = 0; } else if (forceZoom == 1) { cg.snap->ps.zoomMode = 2; } } if (cg.snap->ps.zoomMode == 0) { trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.media.zoomStart ); } else if (cg.snap->ps.zoomMode == 2) { trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.media.zoomEnd ); } } //set the local timing bar extern int cg_genericTimerBar; extern int cg_genericTimerDur; extern vec4_t cg_genericTimerColor; void CG_LocalTimingBar(int startTime, int duration) { cg_genericTimerBar = startTime + duration; cg_genericTimerDur = duration; cg_genericTimerColor[0] = 1.0f; cg_genericTimerColor[1] = 1.0f; cg_genericTimerColor[2] = 0.0f; cg_genericTimerColor[3] = 1.0f; } /* =============== CG_UseItem =============== */ static void CG_UseItem( centity_t *cent ) { clientInfo_t *ci; int itemNum, clientNum; gitem_t *item; entityState_t *es; es = ¢->currentState; itemNum = (es->event & ~EV_EVENT_BITS) - EV_USE_ITEM0; if ( itemNum < 0 || itemNum > HI_NUM_HOLDABLE ) { itemNum = 0; } // print a message if the local player if ( es->number == cg.snap->ps.clientNum ) { if ( !itemNum ) { //CG_CenterPrint( "No item to use", SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); } else { item = BG_FindItemForHoldable( itemNum ); } } switch ( itemNum ) { default: case HI_NONE: //trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.useNothingSound ); break; case HI_BINOCULARS: CG_ToggleBinoculars(cent, es->eventParm); break; case HI_SEEKER: trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.deploySeeker ); break; case HI_SHIELD: case HI_SENTRY_GUN: break; // case HI_MEDKIT: case HI_MEDPAC: case HI_MEDPAC_BIG: clientNum = cent->currentState.clientNum; if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) { ci = &cgs.clientinfo[ clientNum ]; ci->medkitUsageTime = cg.time; } //Different sound for big bacta? trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.medkitSound ); break; case HI_JETPACK: break; //Do something? case HI_HEALTHDISP: //CG_LocalTimingBar(cg.time, TOSS_DEBOUNCE_TIME); break; case HI_AMMODISP: //CG_LocalTimingBar(cg.time, TOSS_DEBOUNCE_TIME); break; case HI_EWEB: break; case HI_CLOAK: break; //Do something? } if (cg.snap && cg.snap->ps.clientNum == cent->currentState.number && itemNum != HI_BINOCULARS && itemNum != HI_JETPACK && itemNum != HI_HEALTHDISP && itemNum != HI_AMMODISP && itemNum != HI_CLOAK && itemNum != HI_EWEB) { //if not using binoculars/jetpack/dispensers/cloak, we just used that item up, so switch BG_CycleInven(&cg.snap->ps, 1); cg.itemSelect = -1; //update the client-side selection display } } /* ================ CG_ItemPickup A new item was picked up this frame ================ */ static void CG_ItemPickup( int itemNum ) { cg.itemPickup = itemNum; cg.itemPickupTime = cg.time; cg.itemPickupBlendTime = cg.time; // see if it should be the grabbed weapon if ( cg.snap && bg_itemlist[itemNum].giType == IT_WEAPON ) { // 0 == no switching // 1 == automatically switch to best SAFE weapon // 2 == automatically switch to best weapon, safe or otherwise // 3 == if not saber, automatically switch to best weapon, safe or otherwise if (0 == cg_autoswitch.integer) { // don't switch } else if ( cg_autoswitch.integer == 1) { //only autoselect if not explosive ("safe") if (bg_itemlist[itemNum].giTag != WP_TRIP_MINE && bg_itemlist[itemNum].giTag != WP_DET_PACK && bg_itemlist[itemNum].giTag != WP_THERMAL && bg_itemlist[itemNum].giTag != WP_ROCKET_LAUNCHER && bg_itemlist[itemNum].giTag > cg.snap->ps.weapon && cg.snap->ps.weapon != WP_SABER) { if (!cg.snap->ps.emplacedIndex) { cg.weaponSelectTime = cg.time; } cg.weaponSelect = bg_itemlist[itemNum].giTag; } } else if ( cg_autoswitch.integer == 2) { //autoselect if better if (bg_itemlist[itemNum].giTag > cg.snap->ps.weapon && cg.snap->ps.weapon != WP_SABER) { if (!cg.snap->ps.emplacedIndex) { cg.weaponSelectTime = cg.time; } cg.weaponSelect = bg_itemlist[itemNum].giTag; } } /* else if ( cg_autoswitch.integer == 3) { //autoselect if better and not using the saber as a weapon if (bg_itemlist[itemNum].giTag > cg.snap->ps.weapon && cg.snap->ps.weapon != WP_SABER) { if (!cg.snap->ps.emplacedIndex) { cg.weaponSelectTime = cg.time; } cg.weaponSelect = bg_itemlist[itemNum].giTag; } } */ //No longer required - just not switching ever if using saber } //rww - print pickup messages if (bg_itemlist[itemNum].classname && bg_itemlist[itemNum].classname[0] && (bg_itemlist[itemNum].giType != IT_TEAM || (bg_itemlist[itemNum].giTag != PW_REDFLAG && bg_itemlist[itemNum].giTag != PW_BLUEFLAG)) ) { //don't print messages for flags, they have their own pickup event broadcasts char text[1024]; char upperKey[1024]; strcpy(upperKey, bg_itemlist[itemNum].classname); if ( trap_SP_GetStringTextString( va("SP_INGAME_%s",Q_strupr(upperKey)), text, sizeof( text ))) { Com_Printf("%s %s\n", CG_GetStringEdString("MP_INGAME", "PICKUPLINE"), text); } else { Com_Printf("%s %s\n", CG_GetStringEdString("MP_INGAME", "PICKUPLINE"), bg_itemlist[itemNum].classname); } } } /* ================ CG_PainEvent Also called by playerstate transition ================ */ void CG_PainEvent( centity_t *cent, int health ) { char *snd; // don't do more than two pain sounds a second if ( cg.time - cent->pe.painTime < 500 ) { return; } if ( health < 25 ) { snd = "*pain25.wav"; } else if ( health < 50 ) { snd = "*pain50.wav"; } else if ( health < 75 ) { snd = "*pain75.wav"; } else { snd = "*pain100.wav"; } trap_S_StartSound( NULL, cent->currentState.number, CHAN_VOICE, CG_CustomSound( cent->currentState.number, snd ) ); // save pain time for programitic twitch animation cent->pe.painTime = cg.time; cent->pe.painDirection ^= 1; } extern qboolean BG_GetRootSurfNameWithVariant( void *ghoul2, const char *rootSurfName, char *returnSurfName, int returnSize ); void CG_ReattachLimb(centity_t *source) { clientInfo_t *ci = NULL; if ( source->currentState.number >= MAX_CLIENTS ) { ci = source->npcClient; } else { ci = &cgs.clientinfo[source->currentState.number]; } if ( ci ) {//re-apply the skin if ( ci->torsoSkin > 0 ) { trap_G2API_SetSkin(source->ghoul2,0,ci->torsoSkin,ci->torsoSkin); } } /* char *limbName; char *stubCapName; int i = G2_MODELPART_HEAD; //rww NOTE: Assumes G2_MODELPART_HEAD is first and G2_MODELPART_RLEG is last while (i <= G2_MODELPART_RLEG) { if (source->torsoBolt & (1 << (i-10))) { switch (i) { case G2_MODELPART_HEAD: limbName = "head"; stubCapName = "torso_cap_head"; break; case G2_MODELPART_WAIST: limbName = "torso"; stubCapName = "hips_cap_torso"; break; case G2_MODELPART_LARM: limbName = "l_arm"; stubCapName = "torso_cap_l_arm"; break; case G2_MODELPART_RARM: limbName = "r_arm"; stubCapName = "torso_cap_r_arm"; break; case G2_MODELPART_RHAND: limbName = "r_hand"; stubCapName = "r_arm_cap_r_hand"; break; case G2_MODELPART_LLEG: limbName = "l_leg"; stubCapName = "hips_cap_l_leg"; break; case G2_MODELPART_RLEG: limbName = "r_leg"; stubCapName = "hips_cap_r_leg"; break; default: source->torsoBolt = 0; source->ghoul2weapon = NULL; return; } trap_G2API_SetSurfaceOnOff(source->ghoul2, limbName, 0); trap_G2API_SetSurfaceOnOff(source->ghoul2, stubCapName, 0x00000100); } i++; } */ source->torsoBolt = 0; source->ghoul2weapon = NULL; } const char *CG_TeamName(int team) { if (team==TEAM_RED) return "RED"; else if (team==TEAM_BLUE) return "BLUE"; else if (team==TEAM_SPECTATOR) return "SPECTATOR"; return "FREE"; } void CG_PrintCTFMessage(clientInfo_t *ci, const char *teamName, int ctfMessage) { char printMsg[1024]; char *refName = NULL; const char *psStringEDString = NULL; switch (ctfMessage) { case CTFMESSAGE_FRAGGED_FLAG_CARRIER: refName = "FRAGGED_FLAG_CARRIER"; break; case CTFMESSAGE_FLAG_RETURNED: refName = "FLAG_RETURNED"; break; case CTFMESSAGE_PLAYER_RETURNED_FLAG: refName = "PLAYER_RETURNED_FLAG"; break; case CTFMESSAGE_PLAYER_CAPTURED_FLAG: refName = "PLAYER_CAPTURED_FLAG"; break; case CTFMESSAGE_PLAYER_GOT_FLAG: refName = "PLAYER_GOT_FLAG"; break; default: return; } psStringEDString = CG_GetStringEdString("MP_INGAME", refName); if (!psStringEDString || !psStringEDString[0]) { return; } if (teamName && teamName[0]) { const char *f = strstr(psStringEDString, "%s"); if (f) { int strLen = 0; int i = 0; if (ci) { Com_sprintf(printMsg, sizeof(printMsg), "%s ", ci->name); strLen = strlen(printMsg); } while (psStringEDString[i] && i < 512) { if (psStringEDString[i] == '%' && psStringEDString[i+1] == 's') { printMsg[strLen] = '\0'; Q_strcat(printMsg, sizeof(printMsg), teamName); strLen = strlen(printMsg); i++; } else { printMsg[strLen] = psStringEDString[i]; strLen++; } i++; } printMsg[strLen] = '\0'; goto doPrint; } } if (ci) { Com_sprintf(printMsg, sizeof(printMsg), "%s %s", ci->name, psStringEDString); } else { Com_sprintf(printMsg, sizeof(printMsg), "%s", psStringEDString); } doPrint: Com_Printf("%s\n", printMsg); } void CG_GetCTFMessageEvent(entityState_t *es) { int clIndex = es->trickedentindex; int teamIndex = es->trickedentindex2; clientInfo_t *ci = NULL; const char *teamName = NULL; if (clIndex < MAX_CLIENTS) { ci = &cgs.clientinfo[clIndex]; } if (teamIndex < 50) { teamName = CG_TeamName(teamIndex); } if (!ci) { return; } CG_PrintCTFMessage(ci, teamName, es->eventParm); } #include "../namespace_begin.h" qboolean BG_InKnockDownOnly( int anim ); #include "../namespace_end.h" //JLFRUMBLE #ifdef _XBOX extern void FF_XboxDamage(int damage, float xpos); #endif void DoFall(centity_t *cent, entityState_t *es, int clientNum) { int delta = es->eventParm; if (cent->currentState.eFlags & EF_DEAD) { //corpses crack into the ground ^_^ if (delta > 25) { trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.fallSound ); } else { trap_S_StartSound (NULL, es->number, CHAN_AUTO, trap_S_RegisterSound( "sound/movers/objects/objectHit.wav" ) ); } } else if (BG_InKnockDownOnly(es->legsAnim)) { if (delta > 14) { trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.fallSound ); } else { trap_S_StartSound (NULL, es->number, CHAN_AUTO, trap_S_RegisterSound( "sound/movers/objects/objectHit.wav" ) ); } } else if (delta > 50) { trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.fallSound ); trap_S_StartSound( NULL, cent->currentState.number, CHAN_VOICE, CG_CustomSound( cent->currentState.number, "*land1.wav" ) ); cent->pe.painTime = cg.time; // don't play a pain sound right after this } else if (delta > 44) { trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.fallSound ); trap_S_StartSound( NULL, cent->currentState.number, CHAN_VOICE, CG_CustomSound( cent->currentState.number, "*land1.wav" ) ); cent->pe.painTime = cg.time; // don't play a pain sound right after this } else { trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.landSound ); } if ( clientNum == cg.predictedPlayerState.clientNum ) { // smooth landing z changes cg.landChange = -delta; if (cg.landChange > 32) { cg.landChange = 32; } if (cg.landChange < -32) { cg.landChange = -32; } cg.landTime = cg.time; } //JLFRUMBLE #ifdef _XBOX if ( cent->playerState) { if (BG_InKnockDownOnly(es->legsAnim)) { if (delta > 14) { FF_XboxDamage(20, 0); } else { FF_XboxDamage(14, 0); } return; } if ( delta > 50) FF_XboxDamage(50, 0); else FF_XboxDamage(delta, 0); /* else if (delta > 44) FF_XboxDamage(44, 0); else FF_XboxDamage(20, 0); */ } #endif } int CG_InClientBitflags(entityState_t *ent, int client) { int checkIn; int sub = 0; if (client > 47) { checkIn = ent->trickedentindex4; sub = 48; } else if (client > 31) { checkIn = ent->trickedentindex3; sub = 32; } else if (client > 15) { checkIn = ent->trickedentindex2; sub = 16; } else { checkIn = ent->trickedentindex; } if (checkIn & (1 << (client-sub))) { return 1; } return 0; } void CG_PlayDoorLoopSound( centity_t *cent ); void CG_PlayDoorSound( centity_t *cent, int type ); void CG_TryPlayCustomSound( vec3_t origin, int entityNum, int channel, const char *soundName ) { sfxHandle_t cSound = CG_CustomSound(entityNum, soundName); if (cSound <= 0) { return; } trap_S_StartSound(origin, entityNum, channel, cSound); } void CG_G2MarkEvent(entityState_t *es) { //es->origin should be the hit location of the projectile, //whereas es->origin2 is the predicted position of the //projectile. (based on the trajectory upon impact) -rww centity_t *pOwner = &cg_entities[es->otherEntityNum]; vec3_t startPoint; float size = 0.0f; qhandle_t shader = 0; if (!pOwner->ghoul2) { //can't do anything then... return; } //es->eventParm being non-0 means to do a special trace check //first. This will give us an impact right at the surface to //project the mark on. Typically this is used for radius //explosions and such, where the source position could be //way outside of model space. if (es->eventParm) { trace_t tr; int ignore = ENTITYNUM_NONE; CG_G2Trace(&tr, es->origin, NULL, NULL, es->origin2, ignore, MASK_PLAYERSOLID); if (tr.entityNum != es->otherEntityNum) { //try again if we hit an ent but not the one we wanted. //CG_TestLine(es->origin, es->origin2, 2000, 0x0000ff, 1); if (tr.entityNum < ENTITYNUM_WORLD) { ignore = tr.entityNum; CG_G2Trace(&tr, es->origin, NULL, NULL, es->origin2, ignore, MASK_PLAYERSOLID); if (tr.entityNum != es->otherEntityNum) { //try extending the trace a bit.. or not /* vec3_t v; VectorSubtract(es->origin2, es->origin, v); VectorScale(v, 64.0f, v); VectorAdd(es->origin2, v, es->origin2); CG_G2Trace(&tr, es->origin, NULL, NULL, es->origin2, ignore, MASK_PLAYERSOLID); if (tr.entityNum != es->otherEntityNum) { return; } */ //didn't manage to collide with the desired person. No mark will be placed then. return; } } } //otherwise we now have a valid starting point. VectorCopy(tr.endpos, startPoint); } else { VectorCopy(es->origin, startPoint); } if ( (es->eFlags&EF_JETPACK_ACTIVE) ) {// a vehicle weapon, make it a larger size mark //OR base this on the size of the thing you hit? if ( g_vehWeaponInfo[es->otherEntityNum2].fG2MarkSize ) { size = flrand( 0.6f, 1.4f )*g_vehWeaponInfo[es->otherEntityNum2].fG2MarkSize; } else { size = flrand( 32.0f, 72.0f ); } //specify mark shader in vehWeapon file if ( g_vehWeaponInfo[es->otherEntityNum2].iG2MarkShaderHandle ) {//have one we want to use instead of defaults shader = g_vehWeaponInfo[es->otherEntityNum2].iG2MarkShaderHandle; } } switch(es->weapon) { case WP_BRYAR_PISTOL: case WP_CONCUSSION: case WP_BRYAR_OLD: case WP_BLASTER: case WP_DISRUPTOR: case WP_BOWCASTER: case WP_REPEATER: case WP_TURRET: if ( !size ) { size = 4.0f; } if ( !shader ) { shader = cgs.media.bdecal_bodyburn1; } CG_AddGhoul2Mark(shader, size, startPoint, es->origin2, es->owner, pOwner->lerpOrigin, pOwner->lerpAngles[YAW], pOwner->ghoul2, pOwner->modelScale, Q_irand(10000, 20000)); break; case WP_ROCKET_LAUNCHER: case WP_THERMAL: if ( !size ) { size = 24.0f; } if ( !shader ) { shader = cgs.media.bdecal_burn1; } CG_AddGhoul2Mark(shader, size, startPoint, es->origin2, es->owner, pOwner->lerpOrigin, pOwner->lerpAngles[YAW], pOwner->ghoul2, pOwner->modelScale, Q_irand(10000, 20000)); break; /* case WP_FLECHETTE: CG_AddGhoul2Mark(cgs.media.bdecal_bodyburn1, flrand(0.5f, 1.0f), startPoint, es->origin2, es->owner, pOwner->lerpOrigin, pOwner->lerpAngles[YAW], pOwner->ghoul2, pOwner->modelScale); break; */ //Issues with small scale? default: break; } } void CG_CalcVehMuzzle(Vehicle_t *pVeh, centity_t *ent, int muzzleNum) { mdxaBone_t boltMatrix; vec3_t vehAngles; assert(pVeh); if (pVeh->m_iMuzzleTime[muzzleNum] == cg.time) { //already done for this frame, don't need to do it again return; } //Uh... how about we set this, hunh...? :) pVeh->m_iMuzzleTime[muzzleNum] = cg.time; VectorCopy( ent->lerpAngles, vehAngles ); if ( pVeh->m_pVehicleInfo ) { if (pVeh->m_pVehicleInfo->type == VH_ANIMAL ||pVeh->m_pVehicleInfo->type == VH_WALKER) { vehAngles[PITCH] = vehAngles[ROLL] = 0.0f; } else if (pVeh->m_pVehicleInfo->type == VH_SPEEDER) { vehAngles[PITCH] = 0.0f; } } trap_G2API_GetBoltMatrix_NoRecNoRot(ent->ghoul2, 0, pVeh->m_iMuzzleTag[muzzleNum], &boltMatrix, vehAngles, ent->lerpOrigin, cg.time, NULL, ent->modelScale); BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, pVeh->m_vMuzzlePos[muzzleNum]); BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_Y, pVeh->m_vMuzzleDir[muzzleNum]); } //corresponds to G_VehMuzzleFireFX -rww void CG_VehMuzzleFireFX(centity_t *veh, entityState_t *broadcaster) { Vehicle_t *pVeh = veh->m_pVehicle; int curMuz = 0, muzFX = 0; if (!pVeh || !veh->ghoul2) { return; } for ( curMuz = 0; curMuz < MAX_VEHICLE_MUZZLES; curMuz++ ) {//go through all muzzles and if ( pVeh->m_iMuzzleTag[curMuz] != -1//valid muzzle bolt && (broadcaster->trickedentindex&(1<m_pVehicleInfo->weapMuzzle[curMuz] == 0 ) {//no weaopon for this muzzle? check turrets int i, j; for ( i = 0; i < MAX_VEHICLE_TURRETS; i++ ) { for ( j = 0; j < MAX_VEHICLE_TURRETS; j++ ) { if ( pVeh->m_pVehicleInfo->turret[i].iMuzzle[j]-1 == curMuz ) {//this muzzle belongs to this turret muzFX = g_vehWeaponInfo[pVeh->m_pVehicleInfo->turret[i].iWeapon].iMuzzleFX; break; } } } } else { muzFX = g_vehWeaponInfo[pVeh->m_pVehicleInfo->weapMuzzle[curMuz]].iMuzzleFX; } if ( muzFX ) { //CG_CalcVehMuzzle(pVeh, veh, curMuz); //trap_FX_PlayEffectID(muzFX, pVeh->m_vMuzzlePos[curMuz], pVeh->m_vMuzzleDir[curMuz], -1, -1); trap_FX_PlayBoltedEffectID(muzFX, veh->currentState.origin, veh->ghoul2, pVeh->m_iMuzzleTag[curMuz], veh->currentState.number, 0, 0, qtrue); } } } } const char *cg_stringEdVoiceChatTable[MAX_CUSTOM_SIEGE_SOUNDS] = { "VC_ATT",//"*att_attack", "VC_ATT_PRIMARY",//"*att_primary", "VC_ATT_SECONDARY",//"*att_second", "VC_DEF_GUNS",//"*def_guns", "VC_DEF_POSITION",//"*def_position", "VC_DEF_PRIMARY",//"*def_primary", "VC_DEF_SECONDARY",//"*def_second", "VC_REPLY_COMING",//"*reply_coming", "VC_REPLY_GO",//"*reply_go", "VC_REPLY_NO",//"*reply_no", "VC_REPLY_STAY",//"*reply_stay", "VC_REPLY_YES",//"*reply_yes", "VC_REQ_ASSIST",//"*req_assist", "VC_REQ_DEMO",//"*req_demo", "VC_REQ_HVY",//"*req_hvy", "VC_REQ_MEDIC",//"*req_medic", "VC_REQ_SUPPLY",//"*req_sup", "VC_REQ_TECH",//"*req_tech", "VC_SPOT_AIR",//"*spot_air", "VC_SPOT_DEF",//"*spot_defenses", "VC_SPOT_EMPLACED",//"*spot_emplaced", "VC_SPOT_SNIPER",//"*spot_sniper", "VC_SPOT_TROOP",//"*spot_troops", "VC_TAC_COVER",//"*tac_cover", "VC_TAC_FALLBACK",//"*tac_fallback", "VC_TAC_FOLLOW",//"*tac_follow", "VC_TAC_HOLD",//"*tac_hold", "VC_TAC_SPLIT",//"*tac_split", "VC_TAC_TOGETHER",//"*tac_together", NULL }; //stupid way of figuring out what string to use for voice chats const char *CG_GetStringForVoiceSound(const char *s) { int i = 0; while (i < MAX_CUSTOM_SIEGE_SOUNDS) { if (bg_customSiegeSoundNames[i] && !Q_stricmp(bg_customSiegeSoundNames[i], s)) { //get the matching reference name assert(cg_stringEdVoiceChatTable[i]); return CG_GetStringEdString("MENUS", (char *)cg_stringEdVoiceChatTable[i]); } i++; } return "voice chat"; } /* ============== CG_EntityEvent An entity has an event value also called by CG_CheckPlayerstateEvents ============== */ #define DEBUGNAME(x) if(cg_debugEvents.integer){CG_Printf(x"\n");} extern void CG_ChatBox_AddString(char *chatStr); //cg_draw.c void CG_EntityEvent( centity_t *cent, vec3_t position ) { entityState_t *es; int event; vec3_t dir; const char *s; int clientNum; clientInfo_t *ci; int eID = 0; int isnd = 0; centity_t *cl_ent; es = ¢->currentState; event = es->event & ~EV_EVENT_BITS; if ( cg_debugEvents.integer ) { CG_Printf( "ent:%3i event:%3i ", es->number, event ); } if ( !event ) { DEBUGNAME("ZEROEVENT"); return; } clientNum = es->clientNum; if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { clientNum = 0; } if (es->eType == ET_NPC) { clientNum = es->number; if (!cent->npcClient) { CG_CreateNPCClient(¢->npcClient); //allocate memory for it if (!cent->npcClient) { assert(0); return; } memset(cent->npcClient, 0, sizeof(clientInfo_t)); cent->npcClient->ghoul2Model = NULL; } ci = cent->npcClient; assert(ci); } else { ci = &cgs.clientinfo[ clientNum ]; } switch ( event ) { // // movement generated events // case EV_CLIENTJOIN: DEBUGNAME("EV_CLIENTJOIN"); //Slight hack to force a local reinit of client entity on join. cl_ent = &cg_entities[es->eventParm]; if (cl_ent) { //cl_ent->torsoBolt = 0; cl_ent->bolt1 = 0; cl_ent->bolt2 = 0; cl_ent->bolt3 = 0; cl_ent->bolt4 = 0; cl_ent->bodyHeight = 0;//SABER_LENGTH_MAX; //cl_ent->saberExtendTime = 0; cl_ent->boltInfo = 0; cl_ent->frame_minus1_refreshed = 0; cl_ent->frame_minus2_refreshed = 0; cl_ent->frame_hold_time = 0; cl_ent->frame_hold_refreshed = 0; cl_ent->trickAlpha = 0; cl_ent->trickAlphaTime = 0; cl_ent->ghoul2weapon = NULL; cl_ent->weapon = WP_NONE; cl_ent->teamPowerEffectTime = 0; cl_ent->teamPowerType = 0; cl_ent->numLoopingSounds = 0; //cl_ent->localAnimIndex = 0; } break; case EV_FOOTSTEP: DEBUGNAME("EV_FOOTSTEP"); if (cg_footsteps.integer) { footstep_t soundType; switch( es->eventParm ) { case MATERIAL_MUD: soundType = FOOTSTEP_MUDWALK; break; case MATERIAL_DIRT: soundType = FOOTSTEP_DIRTWALK; break; case MATERIAL_SAND: soundType = FOOTSTEP_SANDWALK; break; case MATERIAL_SNOW: soundType = FOOTSTEP_SNOWWALK; break; case MATERIAL_SHORTGRASS: case MATERIAL_LONGGRASS: soundType = FOOTSTEP_GRASSWALK; break; case MATERIAL_SOLIDMETAL: soundType = FOOTSTEP_METALWALK; break; case MATERIAL_HOLLOWMETAL: soundType = FOOTSTEP_PIPEWALK; break; case MATERIAL_GRAVEL: soundType = FOOTSTEP_GRAVELWALK; break; case MATERIAL_CARPET: case MATERIAL_FABRIC: case MATERIAL_CANVAS: case MATERIAL_RUBBER: case MATERIAL_PLASTIC: soundType = FOOTSTEP_RUGWALK; break; case MATERIAL_SOLIDWOOD: case MATERIAL_HOLLOWWOOD: soundType = FOOTSTEP_WOODWALK; break; default: soundType = FOOTSTEP_STONEWALK; break; } trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ soundType ][rand()&3] ); } break; case EV_FOOTSTEP_METAL: DEBUGNAME("EV_FOOTSTEP_METAL"); if (cg_footsteps.integer) { trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ FOOTSTEP_METALWALK ][rand()&3] ); } break; case EV_FOOTSPLASH: DEBUGNAME("EV_FOOTSPLASH"); if (cg_footsteps.integer) { trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] ); } break; case EV_FOOTWADE: DEBUGNAME("EV_FOOTWADE"); if (cg_footsteps.integer) { trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] ); } break; case EV_SWIM: DEBUGNAME("EV_SWIM"); if (cg_footsteps.integer) { trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] ); } break; case EV_FALL: DEBUGNAME("EV_FALL"); if (es->number == cg.snap->ps.clientNum && cg.snap->ps.fallingToDeath) { break; } DoFall(cent, es, clientNum); break; case EV_STEP_4: case EV_STEP_8: case EV_STEP_12: case EV_STEP_16: // smooth out step up transitions DEBUGNAME("EV_STEP"); { float oldStep; int delta; int step; if ( clientNum != cg.predictedPlayerState.clientNum ) { break; } // if we are interpolating, we don't need to smooth steps if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) || cg_nopredict.integer || cg_synchronousClients.integer ) { break; } // check for stepping up before a previous step is completed delta = cg.time - cg.stepTime; if (delta < STEP_TIME) { oldStep = cg.stepChange * (STEP_TIME - delta) / STEP_TIME; } else { oldStep = 0; } // add this amount step = 4 * (event - EV_STEP_4 + 1 ); cg.stepChange = oldStep + step; if ( cg.stepChange > MAX_STEP_CHANGE ) { cg.stepChange = MAX_STEP_CHANGE; } cg.stepTime = cg.time; break; } case EV_JUMP_PAD: DEBUGNAME("EV_JUMP_PAD"); break; case EV_GHOUL2_MARK: DEBUGNAME("EV_GHOUL2_MARK"); if (cg_ghoul2Marks.integer) { //Can we put a burn mark on him? CG_G2MarkEvent(es); } break; case EV_GLOBAL_DUEL: DEBUGNAME("EV_GLOBAL_DUEL"); //used for beginning of power duels //if (cg.predictedPlayerState.persistant[PERS_TEAM] != TEAM_SPECTATOR) if (es->otherEntityNum == cg.predictedPlayerState.clientNum || es->otherEntityNum2 == cg.predictedPlayerState.clientNum || es->groundEntityNum == cg.predictedPlayerState.clientNum) { CG_CenterPrint( CG_GetStringEdString("MP_SVGAME", "BEGIN_DUEL"), 120, GIANTCHAR_WIDTH*2 ); trap_S_StartLocalSound( cgs.media.countFightSound, CHAN_ANNOUNCER ); } break; case EV_PRIVATE_DUEL: DEBUGNAME("EV_PRIVATE_DUEL"); if (cg.snap->ps.clientNum != es->number) { break; } if (es->eventParm) { //starting the duel if (es->eventParm == 2) { CG_CenterPrint( CG_GetStringEdString("MP_SVGAME", "BEGIN_DUEL"), 120, GIANTCHAR_WIDTH*2 ); trap_S_StartLocalSound( cgs.media.countFightSound, CHAN_ANNOUNCER ); } else { trap_S_StartBackgroundTrack( "music/mp/duel.mp3", "music/mp/duel.mp3", qfalse ); } } else { //ending the duel CG_StartMusic(qtrue); } break; case EV_JUMP: DEBUGNAME("EV_JUMP"); if (cg_jumpSounds.integer) { trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) ); } break; case EV_ROLL: DEBUGNAME("EV_ROLL"); if (es->number == cg.snap->ps.clientNum && cg.snap->ps.fallingToDeath) { break; } if (es->eventParm) { //fall-roll-in-one event DoFall(cent, es, clientNum); } trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) ); trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.rollSound ); //FIXME: need some sort of body impact on ground sound and maybe kick up some dust? break; case EV_TAUNT: DEBUGNAME("EV_TAUNT"); { int soundIndex = 0; if ( cgs.gametype != GT_DUEL && cgs.gametype != GT_POWERDUEL && es->eventParm == TAUNT_TAUNT ) {//normal taunt soundIndex = CG_CustomSound( es->number, "*taunt.wav" ); } else { switch ( es->eventParm ) { case TAUNT_TAUNT: default: if ( Q_irand( 0, 1 ) ) { soundIndex = CG_CustomSound( es->number, va("*anger%d.wav", Q_irand(1,3)) ); } else { soundIndex = CG_CustomSound( es->number, va("*taunt%d.wav", Q_irand(1,3)) ); if ( !soundIndex ) { soundIndex = CG_CustomSound( es->number, va("*anger%d.wav", Q_irand(1,3)) ); } } break; case TAUNT_BOW: //soundIndex = CG_CustomSound( es->number, va("*respect%d.wav", Q_irand(1,3)) ); break; case TAUNT_MEDITATE: //soundIndex = CG_CustomSound( es->number, va("*meditate%d.wav", Q_irand(1,3)) ); break; case TAUNT_FLOURISH: if ( Q_irand( 0, 1 ) ) { soundIndex = CG_CustomSound( es->number, va("*deflect%d.wav", Q_irand(1,3)) ); if ( !soundIndex ) { soundIndex = CG_CustomSound( es->number, va("*gloat%d.wav", Q_irand(1,3)) ); if ( !soundIndex ) { soundIndex = CG_CustomSound( es->number, va("*anger%d.wav", Q_irand(1,3)) ); } } } else { soundIndex = CG_CustomSound( es->number, va("*gloat%d.wav", Q_irand(1,3)) ); if ( !soundIndex ) { soundIndex = CG_CustomSound( es->number, va("*deflect%d.wav", Q_irand(1,3)) ); if ( !soundIndex ) { soundIndex = CG_CustomSound( es->number, va("*anger%d.wav", Q_irand(1,3)) ); } } } break; case TAUNT_GLOAT: soundIndex = CG_CustomSound( es->number, va("*victory%d.wav", Q_irand(1,3)) ); break; } } if ( !soundIndex ) { soundIndex = CG_CustomSound( es->number, "*taunt.wav" ); } if ( soundIndex ) { trap_S_StartSound (NULL, es->number, CHAN_VOICE, soundIndex ); } } break; //Begin NPC sounds case EV_ANGER1: //Say when acquire an enemy when didn't have one before case EV_ANGER2: case EV_ANGER3: DEBUGNAME("EV_ANGERx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*anger%i.wav", event - EV_ANGER1 + 1) ); break; case EV_VICTORY1: //Say when killed an enemy case EV_VICTORY2: case EV_VICTORY3: DEBUGNAME("EV_VICTORYx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*victory%i.wav", event - EV_VICTORY1 + 1) ); break; case EV_CONFUSE1: //Say when confused case EV_CONFUSE2: case EV_CONFUSE3: DEBUGNAME("EV_CONFUSEDx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*confuse%i.wav", event - EV_CONFUSE1 + 1) ); break; case EV_PUSHED1: //Say when pushed case EV_PUSHED2: case EV_PUSHED3: DEBUGNAME("EV_PUSHEDx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*pushed%i.wav", event - EV_PUSHED1 + 1) ); break; case EV_CHOKE1: //Say when choking case EV_CHOKE2: case EV_CHOKE3: DEBUGNAME("EV_CHOKEx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*choke%i.wav", event - EV_CHOKE1 + 1) ); break; case EV_FFWARN: //Warn ally to stop shooting you DEBUGNAME("EV_FFWARN"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, "*ffwarn.wav" ); break; case EV_FFTURN: //Turn on ally after being shot by them DEBUGNAME("EV_FFTURN"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, "*ffturn.wav" ); break; //extra sounds for ST case EV_CHASE1: case EV_CHASE2: case EV_CHASE3: DEBUGNAME("EV_CHASEx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*chase%i.wav", event - EV_CHASE1 + 1) ); break; case EV_COVER1: case EV_COVER2: case EV_COVER3: case EV_COVER4: case EV_COVER5: DEBUGNAME("EV_COVERx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*cover%i.wav", event - EV_COVER1 + 1) ); break; case EV_DETECTED1: case EV_DETECTED2: case EV_DETECTED3: case EV_DETECTED4: case EV_DETECTED5: DEBUGNAME("EV_DETECTEDx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*detected%i.wav", event - EV_DETECTED1 + 1) ); break; case EV_GIVEUP1: case EV_GIVEUP2: case EV_GIVEUP3: case EV_GIVEUP4: DEBUGNAME("EV_GIVEUPx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*giveup%i.wav", event - EV_GIVEUP1 + 1) ); break; case EV_LOOK1: case EV_LOOK2: DEBUGNAME("EV_LOOKx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*look%i.wav", event - EV_LOOK1 + 1) ); break; case EV_LOST1: DEBUGNAME("EV_LOST1"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, "*lost1.wav" ); break; case EV_OUTFLANK1: case EV_OUTFLANK2: DEBUGNAME("EV_OUTFLANKx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*outflank%i.wav", event - EV_OUTFLANK1 + 1) ); break; case EV_ESCAPING1: case EV_ESCAPING2: case EV_ESCAPING3: DEBUGNAME("EV_ESCAPINGx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*escaping%i.wav", event - EV_ESCAPING1 + 1) ); break; case EV_SIGHT1: case EV_SIGHT2: case EV_SIGHT3: DEBUGNAME("EV_SIGHTx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*sight%i.wav", event - EV_SIGHT1 + 1) ); break; case EV_SOUND1: case EV_SOUND2: case EV_SOUND3: DEBUGNAME("EV_SOUNDx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*sound%i.wav", event - EV_SOUND1 + 1) ); break; case EV_SUSPICIOUS1: case EV_SUSPICIOUS2: case EV_SUSPICIOUS3: case EV_SUSPICIOUS4: case EV_SUSPICIOUS5: DEBUGNAME("EV_SUSPICIOUSx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*suspicious%i.wav", event - EV_SUSPICIOUS1 + 1) ); break; //extra sounds for Jedi case EV_COMBAT1: case EV_COMBAT2: case EV_COMBAT3: DEBUGNAME("EV_COMBATx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*combat%i.wav", event - EV_COMBAT1 + 1) ); break; case EV_JDETECTED1: case EV_JDETECTED2: case EV_JDETECTED3: DEBUGNAME("EV_JDETECTEDx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*jdetected%i.wav", event - EV_JDETECTED1 + 1) ); break; case EV_TAUNT1: case EV_TAUNT2: case EV_TAUNT3: DEBUGNAME("EV_TAUNTx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*taunt%i.wav", event - EV_TAUNT1 + 1) ); break; case EV_JCHASE1: case EV_JCHASE2: case EV_JCHASE3: DEBUGNAME("EV_JCHASEx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*jchase%i.wav", event - EV_JCHASE1 + 1) ); break; case EV_JLOST1: case EV_JLOST2: case EV_JLOST3: DEBUGNAME("EV_JLOSTx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*jlost%i.wav", event - EV_JLOST1 + 1) ); break; case EV_DEFLECT1: case EV_DEFLECT2: case EV_DEFLECT3: DEBUGNAME("EV_DEFLECTx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*deflect%i.wav", event - EV_DEFLECT1 + 1) ); break; case EV_GLOAT1: case EV_GLOAT2: case EV_GLOAT3: DEBUGNAME("EV_GLOATx"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, va("*gloat%i.wav", event - EV_GLOAT1 + 1) ); break; case EV_PUSHFAIL: DEBUGNAME("EV_PUSHFAIL"); CG_TryPlayCustomSound( NULL, es->number, CHAN_VOICE, "*pushfail.wav" ); break; //End NPC sounds case EV_SIEGESPEC: DEBUGNAME("EV_SIEGESPEC"); if ( es->owner == cg.predictedPlayerState.clientNum ) { cg_siegeDeathTime = es->time; } break; case EV_WATER_TOUCH: DEBUGNAME("EV_WATER_TOUCH"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrInSound ); break; case EV_WATER_LEAVE: DEBUGNAME("EV_WATER_LEAVE"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrOutSound ); break; case EV_WATER_UNDER: DEBUGNAME("EV_WATER_UNDER"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrUnSound ); break; case EV_WATER_CLEAR: DEBUGNAME("EV_WATER_CLEAR"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*gasp.wav" ) ); break; case EV_ITEM_PICKUP: DEBUGNAME("EV_ITEM_PICKUP"); { gitem_t *item; int index; qboolean newindex = qfalse; index = cg_entities[es->eventParm].currentState.modelindex; // player predicted if (index < 1 && cg_entities[es->eventParm].currentState.isJediMaster) { //a holocron most likely index = cg_entities[es->eventParm].currentState.trickedentindex4; trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.holocronPickup ); if (es->number == cg.snap->ps.clientNum && showPowersName[index]) { const char *strText = CG_GetStringEdString("MP_INGAME", "PICKUPLINE"); //Com_Printf("%s %s\n", strText, showPowersName[index]); CG_CenterPrint( va("%s %s\n", strText, CG_GetStringEdString("SP_INGAME",showPowersName[index])), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); } //Show the player their force selection bar in case picking the holocron up changed the current selection if (index != FP_SABER_OFFENSE && index != FP_SABER_DEFENSE && index != FP_SABERTHROW && index != FP_LEVITATION && es->number == cg.snap->ps.clientNum && (index == cg.snap->ps.fd.forcePowerSelected || !(cg.snap->ps.fd.forcePowersActive & (1 << cg.snap->ps.fd.forcePowerSelected)))) { if (cg.forceSelect != index) { cg.forceSelect = index; newindex = qtrue; } } if (es->number == cg.snap->ps.clientNum && newindex) { if (cg.forceSelectTime < cg.time) { cg.forceSelectTime = cg.time; } } break; } if (cg_entities[es->eventParm].weapon >= cg.time) { //rww - an unfortunately necessary hack to prevent double item pickups break; } //Hopefully even if this entity is somehow removed and replaced with, say, another //item, this time will have expired by the time that item needs to be picked up. //Of course, it's quite possible this will fail miserably, so if you've got a better //solution then please do use it. cg_entities[es->eventParm].weapon = cg.time+500; if ( index < 1 || index >= bg_numItems ) { break; } item = &bg_itemlist[ index ]; if ( /*item->giType != IT_POWERUP && */item->giType != IT_TEAM) { if (item->pickup_sound && item->pickup_sound[0]) { trap_S_StartSound (NULL, es->number, CHAN_AUTO, trap_S_RegisterSound( item->pickup_sound ) ); } } // show icon and name on status bar if ( es->number == cg.snap->ps.clientNum ) { CG_ItemPickup( index ); } } break; case EV_GLOBAL_ITEM_PICKUP: DEBUGNAME("EV_GLOBAL_ITEM_PICKUP"); { gitem_t *item; int index; index = es->eventParm; // player predicted if ( index < 1 || index >= bg_numItems ) { break; } item = &bg_itemlist[ index ]; // powerup pickups are global if( item->pickup_sound && item->pickup_sound[0] ) { trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( item->pickup_sound) ); } // show icon and name on status bar if ( es->number == cg.snap->ps.clientNum ) { CG_ItemPickup( index ); } } break; case EV_VEH_FIRE: DEBUGNAME("EV_VEH_FIRE"); { centity_t *veh = &cg_entities[es->owner]; CG_VehMuzzleFireFX(veh, es); } break; // // weapon events // case EV_NOAMMO: DEBUGNAME("EV_NOAMMO"); // trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.noAmmoSound ); if ( es->number == cg.snap->ps.clientNum ) { if ( CG_InFighter() || CG_InATST() || cg.snap->ps.weapon == WP_NONE ) {//just letting us know our vehicle is out of ammo //FIXME: flash something on HUD or give some message so we know we have no ammo centity_t *localCent = &cg_entities[cg.snap->ps.clientNum]; if ( localCent->m_pVehicle && localCent->m_pVehicle->m_pVehicleInfo && localCent->m_pVehicle->m_pVehicleInfo->weapon[es->eventParm].soundNoAmmo ) {//play the "no Ammo" sound for this weapon trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, localCent->m_pVehicle->m_pVehicleInfo->weapon[es->eventParm].soundNoAmmo ); } else {//play the default "no ammo" sound trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.media.noAmmoSound ); } //flash the HUD so they associate the sound with the visual indicator that they don't have enough ammo if ( cg_vehicleAmmoWarningTime < cg.time || cg_vehicleAmmoWarning != es->eventParm ) {//if there's already one going, don't interrupt it (unless they tried to fire another weapon that's out of ammo) cg_vehicleAmmoWarning = es->eventParm; cg_vehicleAmmoWarningTime = cg.time+500; } } else if ( cg.snap->ps.weapon == WP_SABER ) { cg.forceHUDTotalFlashTime = cg.time + 1000; } else { int weap = 0; if (es->eventParm && es->eventParm < WP_NUM_WEAPONS) { cg.snap->ps.stats[STAT_WEAPONS] &= ~(1 << es->eventParm); weap = cg.snap->ps.weapon; } else if (es->eventParm) { weap = (es->eventParm-WP_NUM_WEAPONS); } CG_OutOfAmmoChange(weap); } } break; case EV_CHANGE_WEAPON: DEBUGNAME("EV_CHANGE_WEAPON"); { int weapon = es->eventParm; weaponInfo_t *weaponInfo; assert(weapon >= 0 && weapon < MAX_WEAPONS); weaponInfo = &cg_weapons[weapon]; assert(weaponInfo); if (weaponInfo->selectSound) { trap_S_StartSound (NULL, es->number, CHAN_AUTO, weaponInfo->selectSound ); } else if (weapon != WP_SABER) { //not sure what SP is doing for this but I don't want a select sound for saber (it has the saber-turn-on) trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.selectSound ); } } break; case EV_FIRE_WEAPON: DEBUGNAME("EV_FIRE_WEAPON"); if (cent->currentState.number >= MAX_CLIENTS && cent->currentState.eType != ET_NPC) { //special case for turret firing vec3_t gunpoint, gunangle; mdxaBone_t matrix; weaponInfo_t *weaponInfo = &cg_weapons[WP_TURRET]; if ( !weaponInfo->registered ) { CG_RegisterWeapon(WP_TURRET); } if (cent->ghoul2) { if (!cent->bolt1) { cent->bolt1 = trap_G2API_AddBolt(cent->ghoul2, 0, "*flash01"); } if (!cent->bolt2) { cent->bolt2 = trap_G2API_AddBolt(cent->ghoul2, 0, "*flash02"); } trap_G2API_SetBoneAnim(cent->ghoul2, 0, "Bone02", 1, 4, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1.0f, cg.time, -1, 300); } else { break; } if (cent->currentState.eventParm) { trap_G2API_GetBoltMatrix(cent->ghoul2, 0, cent->bolt2, &matrix, cent->currentState.angles, cent->currentState.origin, cg.time, cgs.gameModels, cent->modelScale); } else { trap_G2API_GetBoltMatrix(cent->ghoul2, 0, cent->bolt1, &matrix, cent->currentState.angles, cent->currentState.origin, cg.time, cgs.gameModels, cent->modelScale); } gunpoint[0] = matrix.matrix[0][3]; gunpoint[1] = matrix.matrix[1][3]; gunpoint[2] = matrix.matrix[2][3]; gunangle[0] = -matrix.matrix[0][0]; gunangle[1] = -matrix.matrix[1][0]; gunangle[2] = -matrix.matrix[2][0]; trap_FX_PlayEffectID(cgs.effects.mEmplacedMuzzleFlash, gunpoint, gunangle, -1, -1); } else if (cent->currentState.weapon != WP_EMPLACED_GUN || cent->currentState.eType == ET_NPC) { if (cent->currentState.eType == ET_NPC && cent->currentState.NPC_class == CLASS_VEHICLE && cent->m_pVehicle) { //vehicles do nothing for clientside weapon fire events.. at least for now. break; } CG_FireWeapon( cent, qfalse ); } break; case EV_ALT_FIRE: DEBUGNAME("EV_ALT_FIRE"); if (cent->currentState.weapon == WP_EMPLACED_GUN) { //don't do anything for emplaced stuff break; } if (cent->currentState.eType == ET_NPC && cent->currentState.NPC_class == CLASS_VEHICLE && cent->m_pVehicle) { //vehicles do nothing for clientside weapon fire events.. at least for now. break; } CG_FireWeapon( cent, qtrue ); //if you just exploded your detpacks and you have no ammo left for them, autoswitch if ( cg.snap->ps.clientNum == cent->currentState.number && cg.snap->ps.weapon == WP_DET_PACK ) { if (cg.snap->ps.ammo[weaponData[WP_DET_PACK].ammoIndex] == 0) { CG_OutOfAmmoChange(WP_DET_PACK); } } break; case EV_SABER_ATTACK: DEBUGNAME("EV_SABER_ATTACK"); { qhandle_t swingSound = trap_S_RegisterSound(va("sound/weapons/saber/saberhup%i.wav", Q_irand(1, 8))); clientInfo_t *client = NULL; if ( cg_entities[es->number].currentState.eType == ET_NPC ) { client = cg_entities[es->number].npcClient; } else if ( es->number < MAX_CLIENTS ) { client = &cgs.clientinfo[es->number]; } if ( client && client->infoValid && client->saber[0].swingSound[0] ) {//custom swing sound swingSound = client->saber[0].swingSound[Q_irand(0,2)]; } trap_S_StartSound(es->pos.trBase, es->number, CHAN_WEAPON, swingSound ); } break; case EV_SABER_HIT: DEBUGNAME("EV_SABER_HIT"); { int hitPersonFxID = cgs.effects.mSaberBloodSparks; int hitPersonSmallFxID = cgs.effects.mSaberBloodSparksSmall; int hitPersonMidFxID = cgs.effects.mSaberBloodSparksMid; int hitOtherFxID = cgs.effects.mSaberCut; int hitSound = trap_S_RegisterSound(va("sound/weapons/saber/saberhit%i.wav", Q_irand(1, 3))); if ( es->otherEntityNum2 >= 0 && es->otherEntityNum2 < ENTITYNUM_NONE ) {//we have a specific person who is causing this effect, see if we should override it with any custom saber effects/sounds clientInfo_t *client = NULL; if ( cg_entities[es->otherEntityNum2].currentState.eType == ET_NPC ) { client = cg_entities[es->otherEntityNum2].npcClient; } else if ( es->otherEntityNum2 < MAX_CLIENTS ) { client = &cgs.clientinfo[es->otherEntityNum2]; } if ( client && client->infoValid ) { int saberNum = es->weapon; int bladeNum = es->legsAnim; if ( WP_SaberBladeUseSecondBladeStyle( &client->saber[saberNum], bladeNum ) ) {//use second blade style values if ( client->saber[saberNum].hitPersonEffect2 ) {//custom hit person effect hitPersonFxID = hitPersonSmallFxID = hitPersonMidFxID = client->saber[saberNum].hitPersonEffect2; } if ( client->saber[saberNum].hitOtherEffect2 ) {//custom hit other effect hitOtherFxID = client->saber[saberNum].hitOtherEffect2; } if ( client->saber[saberNum].hit2Sound[0] ) {//custom hit sound hitSound = client->saber[saberNum].hit2Sound[Q_irand(0,2)]; } } else {//use first blade style values if ( client->saber[saberNum].hitPersonEffect ) {//custom hit person effect hitPersonFxID = hitPersonSmallFxID = hitPersonMidFxID = client->saber[saberNum].hitPersonEffect; } if ( client->saber[saberNum].hitOtherEffect ) {//custom hit other effect hitOtherFxID = client->saber[0].hitOtherEffect; } if ( client->saber[saberNum].hitSound[0] ) {//custom hit sound hitSound = client->saber[saberNum].hitSound[Q_irand(0,2)]; } } } } if (es->eventParm == 16) { //Make lots of sparks, something special happened vec3_t fxDir; VectorCopy(es->angles, fxDir); if (!fxDir[0] && !fxDir[1] && !fxDir[2]) { fxDir[1] = 1; } trap_S_StartSound(es->origin, es->number, CHAN_AUTO, hitSound ); trap_FX_PlayEffectID( hitPersonFxID, es->origin, fxDir, -1, -1 ); trap_FX_PlayEffectID( hitPersonFxID, es->origin, fxDir, -1, -1 ); trap_FX_PlayEffectID( hitPersonFxID, es->origin, fxDir, -1, -1 ); trap_FX_PlayEffectID( hitPersonFxID, es->origin, fxDir, -1, -1 ); trap_FX_PlayEffectID( hitPersonFxID, es->origin, fxDir, -1, -1 ); trap_FX_PlayEffectID( hitPersonFxID, es->origin, fxDir, -1, -1 ); } else if (es->eventParm) { //hit a person vec3_t fxDir; VectorCopy(es->angles, fxDir); if (!fxDir[0] && !fxDir[1] && !fxDir[2]) { fxDir[1] = 1; } trap_S_StartSound(es->origin, es->number, CHAN_AUTO, hitSound ); if ( es->eventParm == 3 ) { // moderate or big hits. trap_FX_PlayEffectID( hitPersonSmallFxID, es->origin, fxDir, -1, -1 ); } else if ( es->eventParm == 2 ) { // this is for really big hits. trap_FX_PlayEffectID( hitPersonMidFxID, es->origin, fxDir, -1, -1 ); } else { // this should really just be done in the effect itself, no? trap_FX_PlayEffectID( hitPersonFxID, es->origin, fxDir, -1, -1 ); trap_FX_PlayEffectID( hitPersonFxID, es->origin, fxDir, -1, -1 ); trap_FX_PlayEffectID( hitPersonFxID, es->origin, fxDir, -1, -1 ); } } else { //hit something else vec3_t fxDir; VectorCopy(es->angles, fxDir); if (!fxDir[0] && !fxDir[1] && !fxDir[2]) { fxDir[1] = 1; } //old jk2mp method /* trap_S_StartSound(es->origin, es->number, CHAN_AUTO, trap_S_RegisterSound("sound/weapons/saber/saberhit.wav")); trap_FX_PlayEffectID( trap_FX_RegisterEffect("saber/spark.efx"), es->origin, fxDir, -1, -1 ); */ trap_FX_PlayEffectID( hitOtherFxID, es->origin, fxDir, -1, -1 ); } //rww - this means we have the number of the ent being hit and the ent that owns the saber doing //the hit. This being the case, we can store these indecies and the current time in order to do //some visual tricks on the client between frames to make it look like we're actually continuing //to hit between server frames. if (es->otherEntityNum != ENTITYNUM_NONE && es->otherEntityNum2 != ENTITYNUM_NONE) { centity_t *saberOwner; saberOwner = &cg_entities[es->otherEntityNum2]; saberOwner->serverSaberHitIndex = es->otherEntityNum; saberOwner->serverSaberHitTime = cg.time; if (es->eventParm) { saberOwner->serverSaberFleshImpact = qtrue; } else { saberOwner->serverSaberFleshImpact = qfalse; } } } break; case EV_SABER_BLOCK: DEBUGNAME("EV_SABER_BLOCK"); { if (es->eventParm) { //saber block qboolean cullPass = qfalse; int blockFXID = cgs.effects.mSaberBlock; qhandle_t blockSound = trap_S_RegisterSound(va( "sound/weapons/saber/saberblock%d.wav", Q_irand(1, 9) )); qboolean noFlare = qfalse; if ( es->otherEntityNum2 >= 0 && es->otherEntityNum2 < ENTITYNUM_NONE ) {//we have a specific person who is causing this effect, see if we should override it with any custom saber effects/sounds clientInfo_t *client = NULL; if ( cg_entities[es->otherEntityNum2].currentState.eType == ET_NPC ) { client = cg_entities[es->otherEntityNum2].npcClient; } else if ( es->otherEntityNum2 < MAX_CLIENTS ) { client = &cgs.clientinfo[es->otherEntityNum2]; } if ( client && client->infoValid ) { int saberNum = es->weapon; int bladeNum = es->legsAnim; if ( WP_SaberBladeUseSecondBladeStyle( &client->saber[saberNum], bladeNum ) ) {//use second blade style values if ( client->saber[saberNum].blockEffect2 ) {//custom saber block effect blockFXID = client->saber[saberNum].blockEffect2; } if ( client->saber[saberNum].block2Sound[0] ) {//custom hit sound blockSound = client->saber[saberNum].block2Sound[Q_irand(0,2)]; } } else { if ( client->saber[saberNum].blockEffect ) {//custom saber block effect blockFXID = client->saber[saberNum].blockEffect; } if ( client->saber[saberNum].blockSound[0] ) {//custom hit sound blockSound = client->saber[saberNum].blockSound[Q_irand(0,2)]; } } if ( (client->saber[saberNum].saberFlags2&SFL2_NO_CLASH_FLARE) ) { noFlare = qtrue; } } } if (cg.mInRMG) { trace_t tr; vec3_t vecSub; VectorSubtract(cg.refdef.vieworg, es->origin, vecSub); if (VectorLength(vecSub) < 5000) { CG_Trace(&tr, cg.refdef.vieworg, NULL, NULL, es->origin, ENTITYNUM_NONE, CONTENTS_TERRAIN|CONTENTS_SOLID); if (tr.fraction == 1.0 || tr.entityNum < MAX_CLIENTS) { cullPass = qtrue; } } } else { cullPass = qtrue; } if (cullPass) { vec3_t fxDir; VectorCopy(es->angles, fxDir); if (!fxDir[0] && !fxDir[1] && !fxDir[2]) { fxDir[1] = 1; } trap_S_StartSound(es->origin, es->number, CHAN_AUTO, blockSound ); trap_FX_PlayEffectID( blockFXID, es->origin, fxDir, -1, -1 ); if ( !noFlare ) { cg_saberFlashTime = cg.time-50; VectorCopy( es->origin, cg_saberFlashPos ); } } } else { //projectile block vec3_t fxDir; VectorCopy(es->angles, fxDir); if (!fxDir[0] && !fxDir[1] && !fxDir[2]) { fxDir[1] = 1; } trap_FX_PlayEffectID(cgs.effects.mBlasterDeflect, es->origin, fxDir, -1, -1); } } break; case EV_SABER_CLASHFLARE: DEBUGNAME("EV_SABER_CLASHFLARE"); { qboolean cullPass = qfalse; if (cg.mInRMG) { trace_t tr; vec3_t vecSub; VectorSubtract(cg.refdef.vieworg, es->origin, vecSub); if (VectorLength(vecSub) < 5000) { CG_Trace(&tr, cg.refdef.vieworg, NULL, NULL, es->origin, ENTITYNUM_NONE, CONTENTS_TERRAIN|CONTENTS_SOLID); if (tr.fraction == 1.0 || tr.entityNum < MAX_CLIENTS) { cullPass = qtrue; } } } else { cullPass = qtrue; } if (cullPass) { cg_saberFlashTime = cg.time-50; VectorCopy( es->origin, cg_saberFlashPos ); } trap_S_StartSound ( es->origin, -1, CHAN_WEAPON, trap_S_RegisterSound( va("sound/weapons/saber/saberhitwall%i", Q_irand(1, 3)) ) ); } break; case EV_SABER_UNHOLSTER: DEBUGNAME("EV_SABER_UNHOLSTER"); { clientInfo_t *ci = NULL; if (es->eType == ET_NPC) { ci = cg_entities[es->number].npcClient; } else if (es->number < MAX_CLIENTS) { ci = &cgs.clientinfo[es->number]; } if (ci) { if (ci->saber[0].soundOn) { trap_S_StartSound (NULL, es->number, CHAN_AUTO, ci->saber[0].soundOn ); } if (ci->saber[1].soundOn) { trap_S_StartSound (NULL, es->number, CHAN_AUTO, ci->saber[1].soundOn ); } } } break; case EV_BECOME_JEDIMASTER: DEBUGNAME("EV_SABER_UNHOLSTER"); { trace_t tr; vec3_t playerMins = {-15, -15, DEFAULT_MINS_2+8}; vec3_t playerMaxs = {15, 15, DEFAULT_MAXS_2}; vec3_t ang, pos, dpos; VectorClear(ang); ang[ROLL] = 1; VectorCopy(position, dpos); dpos[2] -= 4096; CG_Trace(&tr, position, playerMins, playerMaxs, dpos, es->number, MASK_SOLID); VectorCopy(tr.endpos, pos); if (tr.fraction == 1) { break; } trap_FX_PlayEffectID(cgs.effects.mJediSpawn, pos, ang, -1, -1); trap_S_StartSound (NULL, es->number, CHAN_AUTO, trap_S_RegisterSound( "sound/weapons/saber/saberon.wav" ) ); if (cg.snap->ps.clientNum == es->number) { trap_S_StartLocalSound(cgs.media.happyMusic, CHAN_LOCAL); CGCam_SetMusicMult(0.3, 5000); } } break; case EV_DISRUPTOR_MAIN_SHOT: DEBUGNAME("EV_DISRUPTOR_MAIN_SHOT"); if (cent->currentState.eventParm != cg.snap->ps.clientNum || cg.renderingThirdPerson) { //h4q3ry CG_GetClientWeaponMuzzleBoltPoint(cent->currentState.eventParm, cent->currentState.origin2); } else { if (cg.lastFPFlashPoint[0] ||cg.lastFPFlashPoint[1] || cg.lastFPFlashPoint[2]) { //get the position of the muzzle flash for the first person weapon model from the last frame VectorCopy(cg.lastFPFlashPoint, cent->currentState.origin2); } } FX_DisruptorMainShot( cent->currentState.origin2, cent->lerpOrigin ); break; case EV_DISRUPTOR_SNIPER_SHOT: DEBUGNAME("EV_DISRUPTOR_SNIPER_SHOT"); if (cent->currentState.eventParm != cg.snap->ps.clientNum || cg.renderingThirdPerson) { //h4q3ry CG_GetClientWeaponMuzzleBoltPoint(cent->currentState.eventParm, cent->currentState.origin2); } else { if (cg.lastFPFlashPoint[0] ||cg.lastFPFlashPoint[1] || cg.lastFPFlashPoint[2]) { //get the position of the muzzle flash for the first person weapon model from the last frame VectorCopy(cg.lastFPFlashPoint, cent->currentState.origin2); } } FX_DisruptorAltShot( cent->currentState.origin2, cent->lerpOrigin, cent->currentState.shouldtarget ); break; case EV_DISRUPTOR_SNIPER_MISS: DEBUGNAME("EV_DISRUPTOR_SNIPER_MISS"); ByteToDir( es->eventParm, dir ); if (es->weapon) { //primary FX_DisruptorHitWall( cent->lerpOrigin, dir ); } else { //secondary FX_DisruptorAltMiss( cent->lerpOrigin, dir ); } break; case EV_DISRUPTOR_HIT: DEBUGNAME("EV_DISRUPTOR_HIT"); ByteToDir( es->eventParm, dir ); if (es->weapon) { //client FX_DisruptorHitPlayer( cent->lerpOrigin, dir, qtrue ); } else { //non-client FX_DisruptorHitWall( cent->lerpOrigin, dir ); } break; case EV_DISRUPTOR_ZOOMSOUND: DEBUGNAME("EV_DISRUPTOR_ZOOMSOUND"); if (es->number == cg.snap->ps.clientNum) { if (cg.snap->ps.zoomMode) { trap_S_StartLocalSound(trap_S_RegisterSound("sound/weapons/disruptor/zoomstart.wav"), CHAN_AUTO); } else { trap_S_StartLocalSound(trap_S_RegisterSound("sound/weapons/disruptor/zoomend.wav"), CHAN_AUTO); } } break; case EV_PREDEFSOUND: DEBUGNAME("EV_PREDEFSOUND"); { int sID = -1; switch (es->eventParm) { case PDSOUND_PROTECTHIT: sID = trap_S_RegisterSound("sound/weapons/force/protecthit.mp3"); break; case PDSOUND_PROTECT: sID = trap_S_RegisterSound("sound/weapons/force/protect.mp3"); break; case PDSOUND_ABSORBHIT: sID = trap_S_RegisterSound("sound/weapons/force/absorbhit.mp3"); if (es->trickedentindex >= 0 && es->trickedentindex < MAX_CLIENTS) { int clnum = es->trickedentindex; cg_entities[clnum].teamPowerEffectTime = cg.time + 1000; cg_entities[clnum].teamPowerType = 3; } break; case PDSOUND_ABSORB: sID = trap_S_RegisterSound("sound/weapons/force/absorb.mp3"); break; case PDSOUND_FORCEJUMP: sID = trap_S_RegisterSound("sound/weapons/force/jump.mp3"); break; case PDSOUND_FORCEGRIP: sID = trap_S_RegisterSound("sound/weapons/force/grip.mp3"); break; default: break; } if (sID != 1) { trap_S_StartSound(es->origin, es->number, CHAN_AUTO, sID); } } break; case EV_TEAM_POWER: DEBUGNAME("EV_TEAM_POWER"); { int clnum = 0; while (clnum < MAX_CLIENTS) { if (CG_InClientBitflags(es, clnum)) { if (es->eventParm == 1) { //eventParm 1 is heal trap_S_StartSound (NULL, clnum, CHAN_AUTO, cgs.media.teamHealSound ); cg_entities[clnum].teamPowerEffectTime = cg.time + 1000; cg_entities[clnum].teamPowerType = 1; } else { //eventParm 2 is force regen trap_S_StartSound (NULL, clnum, CHAN_AUTO, cgs.media.teamRegenSound ); cg_entities[clnum].teamPowerEffectTime = cg.time + 1000; cg_entities[clnum].teamPowerType = 0; } } clnum++; } } break; case EV_SCREENSHAKE: DEBUGNAME("EV_SCREENSHAKE"); if (!es->modelindex || cg.predictedPlayerState.clientNum == es->modelindex-1) { CGCam_Shake(es->angles[0], es->time); } break; case EV_LOCALTIMER: DEBUGNAME("EV_LOCALTIMER"); if (es->owner == cg.predictedPlayerState.clientNum) { CG_LocalTimingBar(es->time, es->time2); } break; case EV_USE_ITEM0: DEBUGNAME("EV_USE_ITEM0"); CG_UseItem( cent ); break; case EV_USE_ITEM1: DEBUGNAME("EV_USE_ITEM1"); CG_UseItem( cent ); break; case EV_USE_ITEM2: DEBUGNAME("EV_USE_ITEM2"); CG_UseItem( cent ); break; case EV_USE_ITEM3: DEBUGNAME("EV_USE_ITEM3"); CG_UseItem( cent ); break; case EV_USE_ITEM4: DEBUGNAME("EV_USE_ITEM4"); CG_UseItem( cent ); break; case EV_USE_ITEM5: DEBUGNAME("EV_USE_ITEM5"); CG_UseItem( cent ); break; case EV_USE_ITEM6: DEBUGNAME("EV_USE_ITEM6"); CG_UseItem( cent ); break; case EV_USE_ITEM7: DEBUGNAME("EV_USE_ITEM7"); CG_UseItem( cent ); break; case EV_USE_ITEM8: DEBUGNAME("EV_USE_ITEM8"); CG_UseItem( cent ); break; case EV_USE_ITEM9: DEBUGNAME("EV_USE_ITEM9"); CG_UseItem( cent ); break; case EV_USE_ITEM10: DEBUGNAME("EV_USE_ITEM10"); CG_UseItem( cent ); break; case EV_USE_ITEM11: DEBUGNAME("EV_USE_ITEM11"); CG_UseItem( cent ); break; case EV_USE_ITEM12: DEBUGNAME("EV_USE_ITEM12"); CG_UseItem( cent ); break; case EV_USE_ITEM13: DEBUGNAME("EV_USE_ITEM13"); CG_UseItem( cent ); break; case EV_USE_ITEM14: DEBUGNAME("EV_USE_ITEM14"); CG_UseItem( cent ); break; case EV_ITEMUSEFAIL: DEBUGNAME("EV_ITEMUSEFAIL"); if (cg.snap->ps.clientNum == es->number) { char *psStringEDRef = NULL; switch(es->eventParm) { case SENTRY_NOROOM: psStringEDRef = (char *)CG_GetStringEdString("MP_INGAME", "SENTRY_NOROOM"); break; case SENTRY_ALREADYPLACED: psStringEDRef = (char *)CG_GetStringEdString("MP_INGAME", "SENTRY_ALREADYPLACED"); break; case SHIELD_NOROOM: psStringEDRef = (char *)CG_GetStringEdString("MP_INGAME", "SHIELD_NOROOM"); break; case SEEKER_ALREADYDEPLOYED: psStringEDRef = (char *)CG_GetStringEdString("MP_INGAME", "SEEKER_ALREADYDEPLOYED"); break; default: break; } if (!psStringEDRef) { break; } Com_Printf("%s\n", psStringEDRef); } break; //================================================================= // // other events // case EV_PLAYER_TELEPORT_IN: DEBUGNAME("EV_PLAYER_TELEPORT_IN"); { trace_t tr; vec3_t playerMins = {-15, -15, DEFAULT_MINS_2+8}; vec3_t playerMaxs = {15, 15, DEFAULT_MAXS_2}; vec3_t ang, pos, dpos; VectorClear(ang); ang[ROLL] = 1; VectorCopy(position, dpos); dpos[2] -= 4096; CG_Trace(&tr, position, playerMins, playerMaxs, dpos, es->number, MASK_SOLID); VectorCopy(tr.endpos, pos); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.teleInSound ); if (tr.fraction == 1) { break; } trap_FX_PlayEffectID(cgs.effects.mSpawn, pos, ang, -1, -1); } break; case EV_PLAYER_TELEPORT_OUT: DEBUGNAME("EV_PLAYER_TELEPORT_OUT"); { trace_t tr; vec3_t playerMins = {-15, -15, DEFAULT_MINS_2+8}; vec3_t playerMaxs = {15, 15, DEFAULT_MAXS_2}; vec3_t ang, pos, dpos; VectorClear(ang); ang[ROLL] = 1; VectorCopy(position, dpos); dpos[2] -= 4096; CG_Trace(&tr, position, playerMins, playerMaxs, dpos, es->number, MASK_SOLID); VectorCopy(tr.endpos, pos); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.teleOutSound ); if (tr.fraction == 1) { break; } trap_FX_PlayEffectID(cgs.effects.mSpawn, pos, ang, -1, -1); } break; case EV_ITEM_POP: DEBUGNAME("EV_ITEM_POP"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound ); break; case EV_ITEM_RESPAWN: DEBUGNAME("EV_ITEM_RESPAWN"); cent->miscTime = cg.time; // scale up from this trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound ); break; case EV_GRENADE_BOUNCE: DEBUGNAME("EV_GRENADE_BOUNCE"); //Do something here? break; case EV_SCOREPLUM: DEBUGNAME("EV_SCOREPLUM"); CG_ScorePlum( cent->currentState.otherEntityNum, cent->lerpOrigin, cent->currentState.time ); break; case EV_CTFMESSAGE: DEBUGNAME("EV_CTFMESSAGE"); CG_GetCTFMessageEvent(es); break; case EV_BODYFADE: if (es->eType != ET_BODY) { assert(!"EV_BODYFADE event from a non-corpse"); break; } if (cent->ghoul2 && trap_G2_HaveWeGhoul2Models(cent->ghoul2)) { //turn the inside of the face off, to avoid showing the mouth when we start alpha fading the corpse trap_G2API_SetSurfaceOnOff( cent->ghoul2, "head_eyes_mouth", 0x00000002/*G2SURFACEFLAG_OFF*/ ); } cent->bodyFadeTime = cg.time + 60000; break; // // siege gameplay events // case EV_SIEGE_ROUNDOVER: DEBUGNAME("EV_SIEGE_ROUNDOVER"); CG_SiegeRoundOver(&cg_entities[cent->currentState.weapon], cent->currentState.eventParm); break; case EV_SIEGE_OBJECTIVECOMPLETE: DEBUGNAME("EV_SIEGE_OBJECTIVECOMPLETE"); CG_SiegeObjectiveCompleted(&cg_entities[cent->currentState.weapon], cent->currentState.eventParm, cent->currentState.trickedentindex); break; case EV_DESTROY_GHOUL2_INSTANCE: DEBUGNAME("EV_DESTROY_GHOUL2_INSTANCE"); if (cg_entities[es->eventParm].ghoul2 && trap_G2_HaveWeGhoul2Models(cg_entities[es->eventParm].ghoul2)) { if (es->eventParm < MAX_CLIENTS) { //You try to do very bad thing! #ifdef _DEBUG Com_Printf("WARNING: Tried to kill a client ghoul2 instance with a server event!\n"); #endif break; } trap_G2API_CleanGhoul2Models(&(cg_entities[es->eventParm].ghoul2)); } break; case EV_DESTROY_WEAPON_MODEL: DEBUGNAME("EV_DESTROY_WEAPON_MODEL"); if (cg_entities[es->eventParm].ghoul2 && trap_G2_HaveWeGhoul2Models(cg_entities[es->eventParm].ghoul2) && trap_G2API_HasGhoul2ModelOnIndex(&(cg_entities[es->eventParm].ghoul2), 1)) { trap_G2API_RemoveGhoul2Model(&(cg_entities[es->eventParm].ghoul2), 1); } break; case EV_GIVE_NEW_RANK: DEBUGNAME("EV_GIVE_NEW_RANK"); if (es->trickedentindex == cg.snap->ps.clientNum) { trap_Cvar_Set("ui_rankChange", va("%i", es->eventParm)); trap_Cvar_Set("ui_myteam", va("%i", es->bolt2)); if (!( trap_Key_GetCatcher() & KEYCATCH_UI ) && !es->bolt1) { trap_OpenUIMenu(UIMENU_PLAYERCONFIG); } } break; case EV_SET_FREE_SABER: DEBUGNAME("EV_SET_FREE_SABER"); trap_Cvar_Set("ui_freeSaber", va("%i", es->eventParm)); break; case EV_SET_FORCE_DISABLE: DEBUGNAME("EV_SET_FORCE_DISABLE"); trap_Cvar_Set("ui_forcePowerDisable", va("%i", es->eventParm)); break; // // missile impacts // case EV_CONC_ALT_IMPACT: DEBUGNAME("EV_CONC_ALT_IMPACT"); { float dist; float shotDist = VectorNormalize(es->angles); vec3_t spot; for (dist = 0.0f; dist < shotDist; dist += 64.0f) { //one effect would be.. a whole lot better VectorMA( es->origin2, dist, es->angles, spot ); trap_FX_PlayEffectID(cgs.effects.mConcussionAltRing, spot, es->angles2, -1, -1); } ByteToDir( es->eventParm, dir ); CG_MissileHitWall(WP_CONCUSSION, es->owner, position, dir, IMPACTSOUND_DEFAULT, qtrue, 0); FX_ConcAltShot(es->origin2, spot); //steal the bezier effect from the disruptor FX_DisruptorAltMiss(position, dir); } break; case EV_MISSILE_STICK: DEBUGNAME("EV_MISSILE_STICK"); // trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.missileStick ); break; case EV_MISSILE_HIT: DEBUGNAME("EV_MISSILE_HIT"); ByteToDir( es->eventParm, dir ); if ( es->emplacedOwner ) {//hack: this is an index to a custom effect to use trap_FX_PlayEffectID(cgs.gameEffects[es->emplacedOwner], position, dir, -1, -1); } else if ( CG_VehicleWeaponImpact( cent ) ) {//a vehicle missile that uses an overridden impact effect... } else if (cent->currentState.eFlags & EF_ALT_FIRING) { CG_MissileHitPlayer( es->weapon, position, dir, es->otherEntityNum, qtrue); } else { CG_MissileHitPlayer( es->weapon, position, dir, es->otherEntityNum, qfalse); } if (cg_ghoul2Marks.integer && es->trickedentindex) { //flag to place a ghoul2 mark CG_G2MarkEvent(es); } break; case EV_MISSILE_MISS: DEBUGNAME("EV_MISSILE_MISS"); ByteToDir( es->eventParm, dir ); if ( es->emplacedOwner ) {//hack: this is an index to a custom effect to use trap_FX_PlayEffectID(cgs.gameEffects[es->emplacedOwner], position, dir, -1, -1); } else if ( CG_VehicleWeaponImpact( cent ) ) {//a vehicle missile that used an overridden impact effect... } else if (cent->currentState.eFlags & EF_ALT_FIRING) { CG_MissileHitWall(es->weapon, 0, position, dir, IMPACTSOUND_DEFAULT, qtrue, es->generic1); } else { CG_MissileHitWall(es->weapon, 0, position, dir, IMPACTSOUND_DEFAULT, qfalse, 0); } if (cg_ghoul2Marks.integer && es->trickedentindex) { //flag to place a ghoul2 mark CG_G2MarkEvent(es); } break; case EV_MISSILE_MISS_METAL: DEBUGNAME("EV_MISSILE_MISS_METAL"); ByteToDir( es->eventParm, dir ); if ( es->emplacedOwner ) {//hack: this is an index to a custom effect to use trap_FX_PlayEffectID(cgs.gameEffects[es->emplacedOwner], position, dir, -1, -1); } else if ( CG_VehicleWeaponImpact( cent ) ) {//a vehicle missile that used an overridden impact effect... } else if (cent->currentState.eFlags & EF_ALT_FIRING) { CG_MissileHitWall(es->weapon, 0, position, dir, IMPACTSOUND_METAL, qtrue, es->generic1); } else { CG_MissileHitWall(es->weapon, 0, position, dir, IMPACTSOUND_METAL, qfalse, 0); } break; case EV_PLAY_EFFECT: DEBUGNAME("EV_PLAY_EFFECT"); switch(es->eventParm) { //it isn't a hack, it's ingenuity! case EFFECT_SMOKE: eID = cgs.effects.mEmplacedDeadSmoke; break; case EFFECT_EXPLOSION: eID = cgs.effects.mEmplacedExplode; break; case EFFECT_EXPLOSION_PAS: eID = cgs.effects.mTurretExplode; break; case EFFECT_SPARK_EXPLOSION: eID = cgs.effects.mSparkExplosion; break; case EFFECT_EXPLOSION_TRIPMINE: eID = cgs.effects.mTripmineExplosion; break; case EFFECT_EXPLOSION_DETPACK: eID = cgs.effects.mDetpackExplosion; break; case EFFECT_EXPLOSION_FLECHETTE: eID = cgs.effects.mFlechetteAltBlow; break; case EFFECT_STUNHIT: eID = cgs.effects.mStunBatonFleshImpact; break; case EFFECT_EXPLOSION_DEMP2ALT: FX_DEMP2_AltDetonate( cent->lerpOrigin, es->weapon ); eID = cgs.effects.mAltDetonate; break; case EFFECT_EXPLOSION_TURRET: eID = cgs.effects.mTurretExplode; break; case EFFECT_SPARKS: eID = cgs.effects.mSparksExplodeNoSound; break; case EFFECT_WATER_SPLASH: eID = cgs.effects.waterSplash; break; case EFFECT_ACID_SPLASH: eID = cgs.effects.acidSplash; break; case EFFECT_LAVA_SPLASH: eID = cgs.effects.lavaSplash; break; case EFFECT_LANDING_MUD: eID = cgs.effects.landingMud; break; case EFFECT_LANDING_SAND: eID = cgs.effects.landingSand; break; case EFFECT_LANDING_DIRT: eID = cgs.effects.landingDirt; break; case EFFECT_LANDING_SNOW: eID = cgs.effects.landingSnow; break; case EFFECT_LANDING_GRAVEL: eID = cgs.effects.landingGravel; break; default: eID = -1; break; } if (eID != -1) { vec3_t fxDir; VectorCopy(es->angles, fxDir); if (!fxDir[0] && !fxDir[1] && !fxDir[2]) { fxDir[1] = 1; } trap_FX_PlayEffectID(eID, es->origin, fxDir, -1, -1); } break; case EV_PLAY_EFFECT_ID: case EV_PLAY_PORTAL_EFFECT_ID: DEBUGNAME("EV_PLAY_EFFECT_ID"); { vec3_t fxDir; qboolean portalEffect = qfalse; int efxIndex = 0; if (event == EV_PLAY_PORTAL_EFFECT_ID) { //This effect should only be played inside sky portals. portalEffect = qtrue; } AngleVectors(es->angles, fxDir, 0, 0); if (!fxDir[0] && !fxDir[1] && !fxDir[2]) { fxDir[1] = 1; } if ( cgs.gameEffects[ es->eventParm ] ) { efxIndex = cgs.gameEffects[es->eventParm]; } else { s = CG_ConfigString( CS_EFFECTS + es->eventParm ); if (s && s[0]) { efxIndex = trap_FX_RegisterEffect(s); } } if (efxIndex) { if (portalEffect) { trap_FX_PlayPortalEffectID(efxIndex, position, fxDir, -1, -1 ); } else { trap_FX_PlayEffectID(efxIndex, position, fxDir, -1, -1 ); } } } break; case EV_PLAYDOORSOUND: CG_PlayDoorSound(cent, es->eventParm); break; case EV_PLAYDOORLOOPSOUND: CG_PlayDoorLoopSound(cent); break; case EV_BMODEL_SOUND: DEBUGNAME("EV_BMODEL_SOUND"); { sfxHandle_t sfx; const char *soundSet; soundSet = CG_ConfigString( CS_AMBIENT_SET + es->soundSetIndex ); if (!soundSet || !soundSet[0]) { break; } sfx = trap_AS_GetBModelSound(soundSet, es->eventParm); if (sfx == -1) { break; } trap_S_StartSound( NULL, es->number, CHAN_AUTO, sfx ); } break; case EV_MUTE_SOUND: DEBUGNAME("EV_MUTE_SOUND"); if (cg_entities[es->trickedentindex2].currentState.eFlags & EF_SOUNDTRACKER) { cg_entities[es->trickedentindex2].currentState.eFlags -= EF_SOUNDTRACKER; } trap_S_MuteSound(es->trickedentindex2, es->trickedentindex); CG_S_StopLoopingSound(es->trickedentindex2, -1); break; case EV_VOICECMD_SOUND: DEBUGNAME("EV_VOICECMD_SOUND"); if (es->groundEntityNum >= MAX_CLIENTS) { //don't ever use this unless it is being used on a real client break; } { sfxHandle_t sfx = cgs.gameSounds[ es->eventParm ]; clientInfo_t *ci = &cgs.clientinfo[es->groundEntityNum]; centity_t *vChatEnt = &cg_entities[es->groundEntityNum]; char descr[1024]; strcpy(descr, CG_GetStringForVoiceSound(CG_ConfigString( CS_SOUNDS + es->eventParm ))); if (!sfx) { s = CG_ConfigString( CS_SOUNDS + es->eventParm ); sfx = CG_CustomSound( es->groundEntityNum, s ); } if (sfx) { if (es->groundEntityNum != cg.predictedPlayerState.clientNum) { //play on the head as well to simulate hearing in radio and in world if (ci->team == cg.predictedPlayerState.persistant[PERS_TEAM]) { //don't hear it if this person is on the other team, but they can still //hear it in the world spot. trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_MENU1, sfx); } } if (ci->team == cg.predictedPlayerState.persistant[PERS_TEAM]) { //add to the chat box //hear it in the world spot. char vchatstr[1024]; strcpy(vchatstr, va("<%s: %s>\n", ci->name, descr)); CG_Printf(vchatstr); CG_ChatBox_AddString(vchatstr); } //and play in world for everyone trap_S_StartSound (NULL, es->groundEntityNum, CHAN_VOICE, sfx); vChatEnt->vChatTime = cg.time + 1000; } } break; case EV_GENERAL_SOUND: DEBUGNAME("EV_GENERAL_SOUND"); if (es->saberEntityNum == TRACK_CHANNEL_2 || es->saberEntityNum == TRACK_CHANNEL_3 || es->saberEntityNum == TRACK_CHANNEL_5) { //channels 2 and 3 are for speed and rage, 5 for sight if ( cgs.gameSounds[ es->eventParm ] ) { CG_S_AddRealLoopingSound(es->number, es->pos.trBase, vec3_origin, cgs.gameSounds[ es->eventParm ] ); } } else { if ( cgs.gameSounds[ es->eventParm ] ) { trap_S_StartSound (NULL, es->number, es->saberEntityNum, cgs.gameSounds[ es->eventParm ] ); } else { s = CG_ConfigString( CS_SOUNDS + es->eventParm ); trap_S_StartSound (NULL, es->number, es->saberEntityNum, CG_CustomSound( es->number, s ) ); } } break; case EV_GLOBAL_SOUND: // play from the player's head so it never diminishes DEBUGNAME("EV_GLOBAL_SOUND"); if ( cgs.gameSounds[ es->eventParm ] ) { trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_MENU1, cgs.gameSounds[ es->eventParm ] ); } else { s = CG_ConfigString( CS_SOUNDS + es->eventParm ); trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_MENU1, CG_CustomSound( es->number, s ) ); } break; case EV_GLOBAL_TEAM_SOUND: // play from the player's head so it never diminishes { DEBUGNAME("EV_GLOBAL_TEAM_SOUND"); switch( es->eventParm ) { case GTS_RED_CAPTURE: // CTF: red team captured the blue flag, 1FCTF: red team captured the neutral flag //CG_AddBufferedSound( cgs.media.redScoredSound ); break; case GTS_BLUE_CAPTURE: // CTF: blue team captured the red flag, 1FCTF: blue team captured the neutral flag //CG_AddBufferedSound( cgs.media.blueScoredSound ); break; case GTS_RED_RETURN: // CTF: blue flag returned, 1FCTF: never used if (cgs.gametype == GT_CTY) { CG_AddBufferedSound( cgs.media.blueYsalReturnedSound ); } else { CG_AddBufferedSound( cgs.media.blueFlagReturnedSound ); } break; case GTS_BLUE_RETURN: // CTF red flag returned, 1FCTF: neutral flag returned if (cgs.gametype == GT_CTY) { CG_AddBufferedSound( cgs.media.redYsalReturnedSound ); } else { CG_AddBufferedSound( cgs.media.redFlagReturnedSound ); } break; case GTS_RED_TAKEN: // CTF: red team took blue flag, 1FCTF: blue team took the neutral flag // if this player picked up the flag then a sound is played in CG_CheckLocalSounds if (cgs.gametype == GT_CTY) { CG_AddBufferedSound( cgs.media.redTookYsalSound ); } else { CG_AddBufferedSound( cgs.media.redTookFlagSound ); } break; case GTS_BLUE_TAKEN: // CTF: blue team took the red flag, 1FCTF red team took the neutral flag // if this player picked up the flag then a sound is played in CG_CheckLocalSounds if (cgs.gametype == GT_CTY) { CG_AddBufferedSound( cgs.media.blueTookYsalSound ); } else { CG_AddBufferedSound( cgs.media.blueTookFlagSound ); } break; case GTS_REDTEAM_SCORED: CG_AddBufferedSound(cgs.media.redScoredSound); break; case GTS_BLUETEAM_SCORED: CG_AddBufferedSound(cgs.media.blueScoredSound); break; case GTS_REDTEAM_TOOK_LEAD: CG_AddBufferedSound(cgs.media.redLeadsSound); break; case GTS_BLUETEAM_TOOK_LEAD: CG_AddBufferedSound(cgs.media.blueLeadsSound); break; case GTS_TEAMS_ARE_TIED: CG_AddBufferedSound( cgs.media.teamsTiedSound ); break; default: break; } break; } case EV_ENTITY_SOUND: DEBUGNAME("EV_ENTITY_SOUND"); //somewhat of a hack - weapon is the caller entity's index, trickedentindex is the proper sound channel if ( cgs.gameSounds[ es->eventParm ] ) { trap_S_StartSound (NULL, es->clientNum, es->trickedentindex, cgs.gameSounds[ es->eventParm ] ); } else { s = CG_ConfigString( CS_SOUNDS + es->eventParm ); trap_S_StartSound (NULL, es->clientNum, es->trickedentindex, CG_CustomSound( es->clientNum, s ) ); } break; case EV_PLAY_ROFF: DEBUGNAME("EV_PLAY_ROFF"); trap_ROFF_Play(es->weapon, es->eventParm, es->trickedentindex); break; case EV_GLASS_SHATTER: DEBUGNAME("EV_GLASS_SHATTER"); CG_GlassShatter(es->genericenemyindex, es->origin, es->angles, es->trickedentindex, es->pos.trTime); break; case EV_DEBRIS: DEBUGNAME("EV_DEBRIS"); CG_Chunks(es->owner, es->origin, es->angles, es->origin2, es->angles2, es->speed, es->eventParm, es->trickedentindex, es->modelindex, es->apos.trBase[0]); break; case EV_MISC_MODEL_EXP: DEBUGNAME("EV_MISC_MODEL_EXP"); CG_MiscModelExplosion(es->origin2, es->angles2, es->time, es->eventParm); break; case EV_PAIN: // local player sounds are triggered in CG_CheckLocalSounds, // so ignore events on the player DEBUGNAME("EV_PAIN"); if ( !cg_oldPainSounds.integer || (cent->currentState.number != cg.snap->ps.clientNum) ) { CG_PainEvent( cent, es->eventParm ); } break; case EV_DEATH1: case EV_DEATH2: case EV_DEATH3: DEBUGNAME("EV_DEATHx"); trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, va("*death%i.wav", event - EV_DEATH1 + 1) ) ); if (es->eventParm && es->number == cg.snap->ps.clientNum) { trap_S_StartLocalSound(cgs.media.dramaticFailure, CHAN_LOCAL); CGCam_SetMusicMult(0.3, 5000); } break; case EV_OBITUARY: DEBUGNAME("EV_OBITUARY"); CG_Obituary( es ); break; // // powerup events // case EV_POWERUP_QUAD: DEBUGNAME("EV_POWERUP_QUAD"); if ( es->number == cg.snap->ps.clientNum ) { cg.powerupActive = PW_QUAD; cg.powerupTime = cg.time; } //trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.quadSound ); break; case EV_POWERUP_BATTLESUIT: DEBUGNAME("EV_POWERUP_BATTLESUIT"); if ( es->number == cg.snap->ps.clientNum ) { cg.powerupActive = PW_BATTLESUIT; cg.powerupTime = cg.time; } //trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.protectSound ); break; case EV_FORCE_DRAINED: DEBUGNAME("EV_FORCE_DRAINED"); ByteToDir( es->eventParm, dir ); //FX_ForceDrained(position, dir); trap_S_StartSound (NULL, es->owner, CHAN_AUTO, cgs.media.drainSound ); cg_entities[es->owner].teamPowerEffectTime = cg.time + 1000; cg_entities[es->owner].teamPowerType = 2; break; case EV_GIB_PLAYER: DEBUGNAME("EV_GIB_PLAYER"); //trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.gibSound ); //CG_GibPlayer( cent->lerpOrigin ); break; case EV_STARTLOOPINGSOUND: DEBUGNAME("EV_STARTLOOPINGSOUND"); if ( cgs.gameSounds[ es->eventParm ] ) { isnd = cgs.gameSounds[es->eventParm]; } else { s = CG_ConfigString( CS_SOUNDS + es->eventParm ); isnd = CG_CustomSound(es->number, s); } CG_S_AddRealLoopingSound( es->number, es->pos.trBase, vec3_origin, isnd ); es->loopSound = isnd; break; case EV_STOPLOOPINGSOUND: DEBUGNAME("EV_STOPLOOPINGSOUND"); CG_S_StopLoopingSound( es->number, -1 ); es->loopSound = 0; break; case EV_WEAPON_CHARGE: DEBUGNAME("EV_WEAPON_CHARGE"); assert(es->eventParm > WP_NONE && es->eventParm < WP_NUM_WEAPONS); if (cg_weapons[es->eventParm].chargeSound) { trap_S_StartSound(NULL, es->number, CHAN_WEAPON, cg_weapons[es->eventParm].chargeSound); } else if (es->eventParm == WP_DISRUPTOR) { trap_S_StartSound(NULL, es->number, CHAN_WEAPON, cgs.media.disruptorZoomLoop); } break; case EV_WEAPON_CHARGE_ALT: DEBUGNAME("EV_WEAPON_CHARGE_ALT"); assert(es->eventParm > WP_NONE && es->eventParm < WP_NUM_WEAPONS); if (cg_weapons[es->eventParm].altChargeSound) { trap_S_StartSound(NULL, es->number, CHAN_WEAPON, cg_weapons[es->eventParm].altChargeSound); } break; case EV_SHIELD_HIT: DEBUGNAME("EV_SHIELD_HIT"); ByteToDir(es->eventParm, dir); CG_PlayerShieldHit(es->otherEntityNum, dir, es->time2); break; case EV_DEBUG_LINE: DEBUGNAME("EV_DEBUG_LINE"); CG_Beam( cent ); break; case EV_TESTLINE: DEBUGNAME("EV_TESTLINE"); CG_TestLine(es->origin, es->origin2, es->time2, es->weapon, 1); break; default: DEBUGNAME("UNKNOWN"); CG_Error( "Unknown event: %i", event ); break; } } /* ============== CG_CheckEvents ============== */ void CG_CheckEvents( centity_t *cent ) { // check for event-only entities if ( cent->currentState.eType > ET_EVENTS ) { if ( cent->previousEvent ) { return; // already fired } // if this is a player event set the entity number of the client entity number if ( cent->currentState.eFlags & EF_PLAYER_EVENT ) { cent->currentState.number = cent->currentState.otherEntityNum; } cent->previousEvent = 1; cent->currentState.event = cent->currentState.eType - ET_EVENTS; } else { // check for events riding with another entity if ( cent->currentState.event == cent->previousEvent ) { return; } cent->previousEvent = cent->currentState.event; if ( ( cent->currentState.event & ~EV_EVENT_BITS ) == 0 ) { return; } } // calculate the position at exactly the frame time BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, cent->lerpOrigin ); CG_SetEntitySoundPosition( cent ); CG_EntityEvent( cent, cent->lerpOrigin ); }