/** * @brief Play a sound on the client side * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg holds the network data * @sa EV_SOUND * @note if the last character of the sound file string that was sent by the * server is a '+', we will select a random sound file by replacing the '+' * character with a number between 01..99 */ void CL_SoundEvent (const eventRegister_t* self, dbuffer* msg) { char sound[MAX_QPATH]; vec3_t origin; int number; int step; /* read data */ NET_ReadFormat(msg, self->formatString, &number, &origin, &step, &sound, sizeof(sound)); le_t* le = LE_Get(number); if (le) { if (LE_IsLivingActor(le) && le->team != cls.team) { /** @todo render */ } else if (LE_IsDoor(le) || LE_IsBreakable(le)) { /** @todo render */ } } const char* file = CL_ConvertSoundFromEvent(sound, sizeof(sound)); Com_DPrintf(DEBUG_SOUND, "Play network sample %s at (%f:%f:%f)\n", file, origin[0], origin[1], origin[2]); if (step >= 0 && step < MAX_ROUTE) { le_t* closest = CL_ActorGetClosest(origin, cls.team); if (closest != nullptr) { vec3_t tmp; VectorCopy(cl.cam.camorg, tmp); VectorCopy(closest->origin, cl.cam.camorg); S_LoadAndPlaySample(file, origin, SOUND_ATTN_NORM, SND_VOLUME_DEFAULT); VectorCopy(tmp, cl.cam.camorg); } return; } S_LoadAndPlaySample(file, origin, SOUND_ATTN_NORM, SND_VOLUME_DEFAULT); }
/** * @note Think function */ static void LET_Projectile (le_t* le) { if (cl.time >= le->endTime) { vec3_t impact; VectorCopy(le->origin, impact); CL_ParticleFree(le->ptl); /* don't run the think function again */ le->inuse = false; if (Q_strvalid(le->ref1)) { VectorCopy(le->ptl->s, impact); le->ptl = CL_ParticleSpawn(le->ref1, 0, impact, bytedirs[le->angle]); VecToAngles(bytedirs[le->state], le->ptl->angles); } if (Q_strvalid(le->ref2)) { S_LoadAndPlaySample(le->ref2, impact, le->fd->impactAttenuation, SND_VOLUME_WEAPONS); } if (le->ref3) { /* Spawn blood particles (if defined) if actor(-body) was hit. Even if actor is dead. */ /** @todo Special particles for stun attack (mind you that there is * electrical and gas/chemical stunning)? */ if (le->fd->obj->dmgtype != csi.damStunGas) LE_ActorBodyHit(le->ref3, impact, le->angle); CL_ActorPlaySound(le->ref3, SND_HURT); } } else if (CL_OutsideMap(le->ptl->s, UNIT_SIZE * 10)) { le->endTime = cl.time; CL_ParticleFree(le->ptl); /* don't run the think function again */ le->inuse = false; } }
/** * @brief Throw item with actor. * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg The netchannel message * @sa EV_ACTOR_THROW */ void CL_ActorDoThrow (const eventRegister_t *self, dbuffer *msg) { vec3_t muzzle, v0; int flags; int dtime; int objIdx; weaponFireDefIndex_t weapFdsIdx; fireDefIndex_t fdIdx; /* read data */ NET_ReadFormat(msg, self->formatString, &dtime, &objIdx, &weapFdsIdx, &fdIdx, &flags, &muzzle, &v0); /* get the fire def */ const objDef_t *obj = INVSH_GetItemByIDX(objIdx); const fireDef_t *fd = FIRESH_GetFiredef(obj, weapFdsIdx, fdIdx); /* add effect le (local entity) */ /** @todo add victim support for blood and hurt sounds */ LE_AddGrenade(fd, flags, muzzle, v0, dtime, nullptr); /* start the sound */ if (fd->fireSound != nullptr && !(flags & SF_BOUNCED)) { S_LoadAndPlaySample(fd->fireSound, muzzle, SOUND_ATTN_IDLE, SND_VOLUME_DEFAULT); } }
void CL_InvReload (const eventRegister_t* self, dbuffer* msg) { int number; int ammo, type, x, y; containerIndex_t container; NET_ReadFormat(msg, self->formatString, &number, &ammo, &type, &container, &x, &y); le_t* le = LE_Get(number); if (!le) return; if (le->team != cls.team) return; assert(container >= 0); assert(container < MAX_INVDEFS); Item* ic = le->inv.getItemAtPos(INVDEF(container), x, y); if (!ic) return; S_LoadAndPlaySample(ic->def()->reloadSound, le->origin, ic->def()->reloadAttenuation, SND_VOLUME_WEAPONS); /* if the displaced clip had any remaining bullets * store them as loose, unless the removed clip was full */ equipDef_t* ed = GAME_GetEquipmentDefinition(); if (ed && ic->getAmmoLeft() > 0 && ic->getAmmoLeft() != ic->def()->ammo) { assert(ammo == ic->def()->ammo); /* Accumulate loose ammo into clips (only accessible post-mission) */ ed->addClip(ic); } /* set new ammo */ ic->setAmmoLeft(ammo); ic->setAmmoDef(INVSH_GetItemByIDX(type)); if (le == selActor) Cmd_ExecuteString("hud_updateactorload"); }
static void LE_PlayFootStepSound (le_t* le) { if (Q_strvalid(le->teamDef->footstepSound)) { S_LoadAndPlaySample(le->teamDef->footstepSound, le->origin, SOUND_ATTN_NORM, SND_VOLUME_FOOTSTEPS); return; } /* walking in water will not play the normal footstep sounds */ if (!le->pathContents[le->pathPos]) { vec3_t from, to; /* prepare trace vectors */ PosToVec(le->pos, from); VectorCopy(from, to); /* we should really hit the ground with this */ to[2] -= UNIT_HEIGHT; const trace_t trace = CL_Trace(Line(from, to), AABB::EMPTY, nullptr, nullptr, MASK_SOLID, cl_worldlevel->integer); if (trace.surface) LE_PlaySoundFileAndParticleForSurface(le, trace.surface->name); } else LE_PlaySoundFileForContents(le, le->pathContents[le->pathPos]); }
/** * @brief Plays step sounds and draw particles for different terrain types * @param[in] le The local entity to play the sound and draw the particle for * @param[in] textureName The name of the texture the actor is standing on * @sa LET_PathMove */ static void LE_PlaySoundFileAndParticleForSurface (le_t* le, const char* textureName) { const terrainType_t* t = Com_GetTerrainType(textureName); if (!t) return; /* origin might not be up-to-date here - but pos should be */ vec3_t origin; PosToVec(le->pos, origin); /** @todo use the Grid_Fall method (ACTOR_SIZE_NORMAL) to ensure, that the particle is * drawn at the ground (if needed - maybe the origin is already ground aligned)*/ if (t->particle) { /* check whether actor is visible */ if (!LE_IsStunned(le) && LE_IsLivingAndVisibleActor(le)) CL_ParticleSpawn(t->particle, 0, origin); } if (t->footstepSound) { Com_DPrintf(DEBUG_SOUND, "LE_PlaySoundFileAndParticleForSurface: volume %.2f\n", t->footstepVolume); S_LoadAndPlaySample(t->footstepSound, origin, SOUND_ATTN_STATIC, t->footstepVolume); } }
/** * @brief Play a sound on the client side * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg holds the network data * @sa EV_SOUND * @note if the last character of the sound file string that was sent by the * server is a '+', we will select a random sound file by replacing the '+' * character with a number between 01..99 */ void CL_SoundEvent (const eventRegister_t *self, struct dbuffer *msg) { char sound[MAX_QPATH]; vec3_t origin; int number; le_t *le; size_t length; /* read data */ NET_ReadFormat(msg, self->formatString, &number, &origin, &sound, sizeof(sound)); le = LE_Get(number); if (le) { if (LE_IsLivingActor(le) && le->team != cls.team) { /** @todo render */ } else if (LE_IsDoor(le) || LE_IsBreakable(le)) { /** @todo render */ } } length = strlen(sound) - 1; if (sound[length] == '+') { int i; sound[length] = '\0'; for (i = 1; i <= 99; i++) { if (FS_CheckFile("sounds/%s%02i", sound, i) == -1) break; } Com_sprintf(sound + length, sizeof(sound) - length, "%02i", rand() % i + 1); } Com_DPrintf(DEBUG_SOUND, "Play network sample %s at (%f:%f:%f)\n", sound, origin[0], origin[1], origin[2]); S_LoadAndPlaySample(sound, origin, SOUND_ATTN_NORM, SND_VOLUME_DEFAULT); }
/** * @brief Shoot with weapon. * @sa CL_ActorShoot * @sa CL_ActorShootHidden * @todo Improve detection of left- or right animation. * @sa EV_ACTOR_SHOOT */ void CL_ActorDoShoot (const eventRegister_t* self, dbuffer* msg) { vec3_t muzzle, impact; int flags, normal, shooterEntnum, victimEntnum; int objIdx; int first; weaponFireDefIndex_t weapFdsIdx; fireDefIndex_t fdIdx; int surfaceFlags; shoot_types_t shootType; /* read data */ NET_ReadFormat(msg, self->formatString, &shooterEntnum, &victimEntnum, &first, &objIdx, &weapFdsIdx, &fdIdx, &shootType, &flags, &surfaceFlags, &muzzle, &impact, &normal); le_t* leVictim; if (victimEntnum != SKIP_LOCAL_ENTITY) { leVictim = LE_Get(victimEntnum); if (!leVictim) LE_NotFoundError(victimEntnum); } else { leVictim = nullptr; } /* get shooter le */ le_t* leShooter = LE_Get(shooterEntnum); /* get the fire def */ const objDef_t* obj = INVSH_GetItemByIDX(objIdx); const fireDef_t* fd = FIRESH_GetFiredef(obj, weapFdsIdx, fdIdx); CL_ActorGetMuzzle(leShooter, muzzle, shootType); /* add effect le */ LE_AddProjectile(fd, flags, muzzle, impact, normal, leVictim); /* start the sound */ if ((first || !fd->soundOnce) && fd->fireSound != nullptr && !(flags & SF_BOUNCED)) S_LoadAndPlaySample(fd->fireSound, muzzle, fd->fireAttenuation, SND_VOLUME_WEAPONS); /* do actor related stuff */ if (!leShooter) return; /* maybe hidden or inuse is false? */ if (!LE_IsActor(leShooter)) Com_Error(ERR_DROP, "Can't shoot, LE not an actor (type: %i)", leShooter->type); /* no animations for hidden actors */ if (leShooter->type == ET_ACTORHIDDEN) return; if (LE_IsDead(leShooter)) { Com_DPrintf(DEBUG_CLIENT, "Can't shoot, actor dead or stunned.\n"); return; } /* Animate - we have to check if it is right or left weapon usage. */ if (IS_SHOT_RIGHT(shootType)) { R_AnimChange(&leShooter->as, leShooter->model1, LE_GetAnim("shoot", leShooter->right, leShooter->left, leShooter->state)); R_AnimAppend(&leShooter->as, leShooter->model1, LE_GetAnim("stand", leShooter->right, leShooter->left, leShooter->state)); } else if (IS_SHOT_LEFT(shootType)) { R_AnimChange(&leShooter->as, leShooter->model1, LE_GetAnim("shoot", leShooter->left, leShooter->right, leShooter->state)); R_AnimAppend(&leShooter->as, leShooter->model1, LE_GetAnim("stand", leShooter->left, leShooter->right, leShooter->state)); } else if (IS_SHOT_HEADGEAR(shootType)) { if (fd->irgoggles) { leShooter->state |= RF_IRGOGGLESSHOT; if (LE_IsSelected(leShooter)) refdef.rendererFlags |= RDF_IRGOGGLES; } } else { /* no animation for headgear (yet) */ Com_Error(ERR_DROP, "CL_ActorDoShoot: Invalid shootType given (entnum: %i, shootType: %i).\n", shootType, shooterEntnum); } }
void LE_AddProjectile (const fireDef_t* fd, int flags, const vec3_t muzzle, const vec3_t impact, int normal, le_t* leVictim) { /* add le */ le_t* le = LE_Add(0); if (!le) return; LE_SetInvisible(le); /* bind particle */ le->ptl = CL_ParticleSpawn(fd->projectile, 0, muzzle); if (!le->ptl) { le->inuse = false; return; } /* calculate parameters */ vec3_t delta; VectorSubtract(impact, muzzle, delta); const float dist = VectorLength(delta); VecToAngles(delta, le->ptl->angles); /* direction - bytedirs index */ le->angle = normal; le->fd = fd; /* infinite speed projectile? */ if (!fd->speed) { le->inuse = false; le->ptl->size[0] = dist; VectorMA(muzzle, 0.5, delta, le->ptl->s); if ((flags & (SF_IMPACT | SF_BODY)) || (fd->splrad && !fd->bounce)) { ptl_t* ptl = nullptr; const float* dir = bytedirs[le->angle]; if (flags & SF_BODY) { if (fd->hitBodySound != nullptr) { S_LoadAndPlaySample(fd->hitBodySound, le->origin, le->fd->impactAttenuation, SND_VOLUME_WEAPONS); } if (fd->hitBody != nullptr) ptl = CL_ParticleSpawn(fd->hitBody, 0, impact, dir); /* Spawn blood particles (if defined) if actor(-body) was hit. Even if actor is dead. */ /** @todo Special particles for stun attack (mind you that there is * electrical and gas/chemical stunning)? */ if (leVictim) { if (fd->obj->dmgtype != csi.damStunGas) LE_ActorBodyHit(leVictim, impact, le->angle); if (fd->damage[0] >= 0) CL_ActorPlaySound(leVictim, SND_HURT); } } else { if (fd->impactSound != nullptr) { S_LoadAndPlaySample(fd->impactSound, le->origin, le->fd->impactAttenuation, SND_VOLUME_WEAPONS); } if (fd->impact != nullptr) ptl = CL_ParticleSpawn(fd->impact, 0, impact, dir); } if (ptl) VecToAngles(dir, ptl->angles); } return; } /* particle properties */ VectorScale(delta, fd->speed / dist, le->ptl->v); le->endTime = cl.time + 1000 * dist / fd->speed; /* think function */ if (flags & SF_BODY) { le->ref1 = fd->hitBody; le->ref2 = fd->hitBodySound; le->ref3 = leVictim; } else if ((flags & SF_IMPACT) || (fd->splrad && !fd->bounce)) { le->ref1 = fd->impact; le->ref2 = fd->impactSound; } else { le->ref1 = nullptr; if (flags & SF_BOUNCING) le->ref2 = fd->bounceSound; } LE_SetThink(le, LET_Projectile); LE_ExecuteThink(le); }