diff --git a/common/const.h b/common/const.h index 323ff96bb..e88234206 100644 --- a/common/const.h +++ b/common/const.h @@ -129,6 +129,7 @@ // entity flags #define EFLAG_SLERP 1 // do studio interpolation of this entity #define EFLAG_FLESH_SOUND 2 +#define EFLAG_PREVENT_ORIGIN_UNSETTING 4 // // temp entity events diff --git a/dlls/client.cpp b/dlls/client.cpp index 0da56a92b..dc6186f8a 100644 --- a/dlls/client.cpp +++ b/dlls/client.cpp @@ -1605,9 +1605,12 @@ void Entity_Encode( struct delta_s *pFields, const unsigned char *from, const un if( ( t->movetype == MOVETYPE_FOLLOW ) && ( t->aiment != 0 ) ) { - DELTA_UNSETBYINDEX( pFields, entity_field_alias[FIELD_ORIGIN0].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[FIELD_ORIGIN1].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[FIELD_ORIGIN2].field ); + if ((t->eflags & EFLAG_PREVENT_ORIGIN_UNSETTING) == 0) + { + DELTA_UNSETBYINDEX( pFields, entity_field_alias[FIELD_ORIGIN0].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[FIELD_ORIGIN1].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[FIELD_ORIGIN2].field ); + } } else if( t->aiment != f->aiment ) { diff --git a/dlls/sound.cpp b/dlls/sound.cpp index bed74cc10..0b33d5ebc 100644 --- a/dlls/sound.cpp +++ b/dlls/sound.cpp @@ -150,8 +150,8 @@ class CAmbientGeneric : public CBaseEntity int m_iChannel; //LRC - the channel to play from, for "play from X" sounds EHANDLE m_hActivator; // this is for m_hPlayFrom, in case the entity is !activator - bool EntityToPlayFromIsDefined(); - CBaseEntity* GetEntityToPlayFrom(CBaseEntity* pActivator); + virtual bool EntityToPlayFromIsDefined(); + virtual CBaseEntity* GetEntityToPlayFrom(CBaseEntity* pActivator); }; LINK_ENTITY_TO_CLASS( ambient_generic, CAmbientGeneric ) @@ -290,14 +290,11 @@ void CAmbientGeneric::Precache( void ) m_fActive = TRUE; } - if( m_fActive ) + if (m_fActive && !EntityToPlayFromIsDefined()) { - if (!EntityToPlayFromIsDefined()) - { - UTIL_EmitAmbientSound( ENT( pev ), pev->origin, szSoundFile, - ( m_dpv.vol * 0.01f ), m_flAttenuation, SND_SPAWNING, m_dpv.pitch ); - pev->nextthink = gpGlobals->time + 0.1f; - } + UTIL_EmitAmbientSound( ENT( pev ), pev->origin, szSoundFile, + ( m_dpv.vol * 0.01f ), m_flAttenuation, SND_SPAWNING, m_dpv.pitch ); + pev->nextthink = gpGlobals->time + 0.1f; } } @@ -318,8 +315,8 @@ void CAmbientGeneric::Activate() CBaseEntity *pTarget = GetEntityToPlayFrom(m_hActivator); if (!pTarget) { - ALERT(at_console, "WARNING: ambient_generic \"%s\" can't find \"%s\", its entity to play from.\n", - STRING(pev->targetname), STRING(pev->target)); + ALERT(at_console, "WARNING: %s \"%s\" can't find \"%s\", its entity to play from.\n", + STRING(pev->classname), STRING(pev->targetname), STRING(pev->target)); } else { @@ -2171,3 +2168,99 @@ void CEnvSoundMark::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE UTIL_Remove(this); } } + +class CExtraSpeaker : public CPointEntity +{ +public: + void Think() + { + // Gargbage collection + CBaseEntity* pOwner = nullptr; + if (!FNullEnt(pev->owner)) + { + pOwner = CBaseEntity::Instance(pev->owner); + } + if (!pOwner) + { + ALERT(at_console, "Removing %s because the owner has expired\n", STRING(pev->classname)); + UTIL_Remove(this); + return; + } + pev->nextthink = gpGlobals->time + 1.0f; + } +}; + +LINK_ENTITY_TO_CLASS( extra_speaker, CExtraSpeaker ) + +class CAmbientExtraSpeaker : public CAmbientGeneric +{ +public: + CBaseEntity* GetEntityToPlayFrom(CBaseEntity *pActivator); + bool EntityToPlayFromIsDefined() { return true; } + +protected: + CBaseEntity* GetTargetEntity(CBaseEntity* pActivator); + CBaseEntity* GetSpeakerEntity(CBaseEntity* pTargetEntity); +}; + +LINK_ENTITY_TO_CLASS( ambient_extraspeaker, CAmbientExtraSpeaker ) + +CBaseEntity* CAmbientExtraSpeaker::GetEntityToPlayFrom(CBaseEntity *pActivator) +{ + CBaseEntity* pTargetEntity = GetTargetEntity(pActivator); + if (!pTargetEntity) + return nullptr; + + CBaseEntity* pSpeakerEntity = GetSpeakerEntity(pTargetEntity); + if (pSpeakerEntity) + { + pSpeakerEntity->pev->movetype = MOVETYPE_FOLLOW; + pSpeakerEntity->pev->aiment = pTargetEntity->edict(); + } + return pSpeakerEntity; +} + +CBaseEntity* CAmbientExtraSpeaker::GetTargetEntity(CBaseEntity *pActivator) +{ + CBaseEntity* pTargetEntity = nullptr; + if (FStringNull(pev->target)) + { + pTargetEntity = g_pGameRules->EffectivePlayer(pActivator); + } + else + { + pTargetEntity = UTIL_FindEntityByTargetname(nullptr, STRING(pev->target), pActivator); + } + return pTargetEntity; +} + +CBaseEntity* CAmbientExtraSpeaker::GetSpeakerEntity(CBaseEntity *pTargetEntity) +{ + CBaseEntity* pEntity = nullptr; + while((pEntity = UTIL_FindEntityByClassname(pEntity, "extra_speaker")) != nullptr) + { + if (pTargetEntity->edict() == pEntity->pev->owner) + { + return pEntity; + } + } + + if (!pEntity) { + pEntity = CBaseEntity::CreateNoSpawn("extra_speaker", pTargetEntity->pev->origin, pTargetEntity->pev->angles); + + if (pEntity) { + ALERT(at_console, "Created %s for %s\n", STRING(pEntity->pev->classname), STRING(pTargetEntity->pev->classname)); + pEntity->pev->movetype = MOVETYPE_FOLLOW; + pEntity->pev->aiment = pTargetEntity->edict(); + pEntity->pev->owner = pTargetEntity->edict(); + pEntity->m_EFlags |= EFLAG_PREVENT_ORIGIN_UNSETTING; + DispatchSpawn(pEntity->edict()); + SET_MODEL(pEntity->edict(), "sprites/iunknown.spr"); + pEntity->pev->rendermode = kRenderTransAlpha; + pEntity->pev->renderamt = 0; + pEntity->pev->nextthink = gpGlobals->time + 1.0f; + } + } + + return pEntity; +} diff --git a/fgd/halflife.fgd b/fgd/halflife.fgd index b28bc47cc..ccaa67d40 100644 --- a/fgd/halflife.fgd +++ b/fgd/halflife.fgd @@ -1153,11 +1153,15 @@ @PointClass base(ScriptedSequence) = aiscripted_sequence:"AI Scripted Sequence" [] -@BaseClass iconsprite("sprites/ambient_generic.spr") base(Targetname) = Ambient +@BaseClass base(Targetname) = AmbientBase [ message(sound) : "WAV Name" health(integer) : "Volume (10 = loudest)" : 10 - target(target_destination) : "Entity to play from" +] + +@BaseClass = AmbientPlayFrom +[ + target(target_generic) : "Entity to play from" channel(choices) : "Channel to use for that entity" = [ 0: "Default (Static)" @@ -1168,6 +1172,10 @@ 5: "Stream" 6: "Static" ] +] + +@BaseClass = AmbientDetails +[ preset(choices) :"Dynamic Presets" = [ 0: "None" @@ -1232,9 +1240,33 @@ ] ] -@PointClass base(Ambient) = ambient_generic : "Universal Ambient" [] +@BaseClass = AmbientExtraSpeaker +[ + target(string) : "Attach to entity" : "*player" + channel(choices) : "Channel to use" = + [ + 0: "Default (Static)" + 1: "Weapon" + 2: "Voice" + 3: "Item" + 4: "Body" + 5: "Stream" + 6: "Static" + ] +] + +@PointClass iconsprite("sprites/ambient_generic.spr") base(AmbientBase, AmbientExtraSpeaker, AmbientDetails) = ambient_extraspeaker : "Ambient played on the automatically created entity that is attached to another entity. This allows to play sounds like if they were playing from the main entity without interfering with its own audio channels. Use this for radio or narrator messages that should follow the player around." +[ + spawnflags(flags) = + [ + 16 : "Start Silent": 1 + 32 : "Not Toggled": 1 + ] +] + +@PointClass iconsprite("sprites/ambient_generic.spr") base(AmbientBase, AmbientPlayFrom, AmbientDetails) = ambient_generic : "Universal Ambient" [] -@PointClass base(Ambient) = ambient_random : "Universal Ambient playing a random sound" +@BaseClass = AmbientRandomWavs [ noise(sound) : "WAV Name 1" noise1(sound) : "WAV Name 2" @@ -1242,6 +1274,8 @@ noise3(sound) : "WAV Name 4" ] +@PointClass iconsprite("sprites/ambient_generic.spr") base(AmbientBase, AmbientRandomWavs, AmbientPlayFrom, AmbientDetails) = ambient_random : "Universal Ambient playing a random sound" [] + // // ammo //