コード例 #1
0
//------------------------------------------------------------------------
void CItem::StopSound(tSoundID id)
{
	if(id == INVALID_SOUNDID)
		return;

	bool synchSound = false;
	IEntitySoundProxy *pSoundProxy = GetSoundProxy(false);

	if(pSoundProxy)
	{
		for(TInstanceActionMap::iterator it = m_instanceActions.begin(); it != m_instanceActions.end(); ++it)
		{
			SInstanceAction &action = it->second;

			for(int i=0; i<2; i++)
			{
				if(action.sound[i].id == id)
				{
					pSoundProxy->SetStaticSound(id, false);
					action.sound[i].id = INVALID_SOUNDID;
					synchSound = action.sound[i].synch;
					break;
				}
			}
		}

		if(synchSound)
			pSoundProxy->StopSound(id, ESoundStopMode_OnSyncPoint);
		else
			pSoundProxy->StopSound(id);
	}
}
コード例 #2
0
ファイル: Tactical.cpp プロジェクト: RenEvo/dead6
//------------------------------------------------------------------------
void SSoundEffect::Activate(EntityId targetId, EntityId ownerId, EntityId weaponId, const char *effect, const char *defaultEffect)
{
	// TODO: make work in MP...

	IEntity* pEntity = gEnv->pEntitySystem->GetEntity(targetId);
	IEntity* pOwnerEntity = gEnv->pEntitySystem->GetEntity(ownerId);
	
	if(pEntity && gEnv->pSoundSystem && pOwnerEntity)
	{
		int		soundFlag = FLAG_SOUND_3D; 
		bool	repeating = false;
		bool	force3DSound = true;

		IEntitySoundProxy* pSoundProxy = (IEntitySoundProxy*)pOwnerEntity->GetProxy(ENTITY_PROXY_SOUND);

		if(pSoundProxy)
		{
			//Execute sound at entity position
			GameWarning("Sound effect: %s (Entity)", defaultEffect);
			pSoundProxy->PlaySound(defaultEffect, pEntity->GetWorldPos()-pOwnerEntity->GetWorldPos(),FORWARD_DIRECTION, FLAG_SOUND_DEFAULT_3D);

			// Notify AI system (TODO: Parametrize radius)
			gEnv->pAISystem->SoundEvent(pEntity->GetWorldPos(), 100.0f, AISE_GENERIC, NULL);
			//gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere(pEntity->GetWorldPos(),0.5f,ColorB(255,0,0));
		}
	}
}
コード例 #3
0
ファイル: ItemResource.cpp プロジェクト: eBunny/EmberProject
//------------------------------------------------------------------------
ISound *CItem::GetISound(tSoundID id)
{
    IEntitySoundProxy *pSoundProxy = GetSoundProxy(false);
    if (pSoundProxy)
        return pSoundProxy->GetSound(id);

    return 0;
}
コード例 #4
0
ファイル: ItemResource.cpp プロジェクト: eBunny/EmberProject
//------------------------------------------------------------------------
void CItem::Quiet()
{
    IEntitySoundProxy *pSoundProxy = GetSoundProxy(false);
    if (pSoundProxy)
        {
            pSoundProxy->StopAllSounds();
        }
}
コード例 #5
0
tSoundID CHeavyMountedWeapon::PlayRotationSound()
{
	tSoundID result = 0;
	IEntitySoundProxy* pSoundProxy = GetSoundProxy(true);
	if (pSoundProxy)
	{
		const ItemString& soundName = m_stats.fp ? m_sharedparams->pMountParams->rotate_sound_fp : m_sharedparams->pMountParams->rotate_sound_tp;
		result = pSoundProxy->PlaySoundEx(soundName.c_str(), ZERO, FORWARD_DIRECTION, FLAG_SOUND_DEFAULT_3D, 0, 1.0f, 0, 0, eSoundSemantic_Weapon);
	}
	return result;
}
コード例 #6
0
//------------------------------------------------------------------------
void CGameRulesKingOfTheHillObjective::ClearEntityAudio( SHoldEntityDetails *pDetails )
{
	IEntity *pEntity = gEnv->pEntitySystem->GetEntity(pDetails->m_id);
	if(pEntity)
	{
		IEntitySoundProxy *soundProxy = (IEntitySoundProxy*) pEntity->GetProxy(ENTITY_PROXY_SOUND);
		if(soundProxy)
		{
			soundProxy->StopAllSounds();
		}
	}
}
コード例 #7
0
ファイル: BoidObject.cpp プロジェクト: AiYong/CryGame
void CBoidObject::PlaySound( int nIndex )
{
	if (nIndex >= 0 && nIndex < (int)m_flock->m_bc.sounds.size())
	{
		IEntity *pEntity = gEnv->pEntitySystem->GetEntity(m_entity);
		if (pEntity)
		{
			IEntitySoundProxy* pSndProxy = (IEntitySoundProxy*)pEntity->CreateProxy(ENTITY_PROXY_SOUND);
			Vec3 ofs(0,0,0),dir(FORWARD_DIRECTION);
			pSndProxy->PlaySound( m_flock->m_bc.sounds[nIndex],ofs,dir,FLAG_SOUND_DEFAULT_3D, 0, eSoundSemantic_Living_Entity );
		}
	}
}
コード例 #8
0
ファイル: ItemResource.cpp プロジェクト: eBunny/EmberProject
//------------------------------------------------------------------------
void CItem::StopSound(tSoundID id)
{
    if (id == INVALID_SOUNDID)
        return;

    bool synchSound = false;
    IEntitySoundProxy *pSoundProxy = GetSoundProxy(false);
    if (pSoundProxy)
        {
            if(synchSound)
                pSoundProxy->StopSound(id, ESoundStopMode_OnSyncPoint);
            else
                pSoundProxy->StopSound(id);
        }
}
コード例 #9
0
ファイル: ItemResource.cpp プロジェクト: kitnet/project-o
//------------------------------------------------------------------------
void CItem::ReleaseStaticSound(SInstanceAudio *sound)
{
	if (sound->id != INVALID_SOUNDID)
	{
		IEntitySoundProxy *pSoundProxy = GetSoundProxy(false);
		if (pSoundProxy)
		{
			pSoundProxy->SetStaticSound(sound->id, false);
			if(sound->synch)
				pSoundProxy->StopSound(sound->id,ESoundStopMode_OnSyncPoint);
			else
				pSoundProxy->StopSound(sound->id);
			sound->id = INVALID_SOUNDID;
#ifndef ITEM_USE_SHAREDSTRING
			sound->static_name.resize(0);
#else
			sound->static_name.reset();
#endif
		}
	}
}
コード例 #10
0
ファイル: ItemResource.cpp プロジェクト: kitnet/project-o
//------------------------------------------------------------------------
void CItem::Quiet()
{
	IEntitySoundProxy *pSoundProxy = GetSoundProxy(false);
	if (pSoundProxy)
	{
		for (TInstanceActionMap::iterator it = m_instanceActions.begin(); it != m_instanceActions.end(); ++it)
		{
			SInstanceAction &action = it->second;
			for (int i=0;i<2;i++)
			{
				if (action.sound[i].id != INVALID_SOUNDID)
				{
					pSoundProxy->SetStaticSound(action.sound[i].id, false);
					action.sound[i].id = INVALID_SOUNDID;
				}
			}
		}

		pSoundProxy->StopAllSounds();
	}
}
コード例 #11
0
ファイル: AmmoPickup.cpp プロジェクト: mrwonko/CrysisVR
//------------------------------------------------------------------------
void CAmmoPickup::PickUp(EntityId pickerId, bool sound, bool select, bool keepHistory)
{
	if(!CheckAmmoRestrictions(pickerId))
		return;

	SetOwnerId(pickerId);

	CActor *pActor=GetActor(pickerId);

	if (!pActor)
		return;

	IInventory *pInventory = GetActorInventory(pActor);
	if (!pInventory)
		return;

	if (IsServer())
	{
		// bonus ammo is always put in the actor's inv
		if (!m_bonusammo.empty())
		{
			for (TAmmoMap::iterator it=m_bonusammo.begin(); it!=m_bonusammo.end(); ++it)
			{
				int count=it->second;

				SetInventoryAmmoCount(it->first, GetInventoryAmmoCount(it->first)+count);

				if(pActor->IsPlayer())
				{
					ShouldSwitchGrenade(it->first);
					OnIncendiaryAmmoPickedUp(it->first,count);
				}
			}

			m_bonusammo.clear();
		}

		for (TAmmoMap::iterator it=m_ammo.begin(); it!=m_ammo.end(); ++it)
		{
			int count=it->second;

			SetInventoryAmmoCount(it->first, GetInventoryAmmoCount(it->first)+count);

			if(pActor->IsPlayer())
			{
				ShouldSwitchGrenade(it->first);
				OnIncendiaryAmmoPickedUp(it->first,count);
			}
		}

		if (!m_ammoName.empty() && m_ammoCount)
		{
			IEntityClass* pClass = gEnv->pEntitySystem->GetClassRegistry()->FindClass(m_ammoName.c_str());
			SetInventoryAmmoCount(pClass, GetInventoryAmmoCount(pClass)+m_ammoCount);

			if(pActor->IsPlayer())
			{
				ShouldSwitchGrenade(pClass);
				OnIncendiaryAmmoPickedUp(pClass,m_ammoCount);
			}
		}

		TriggerRespawn();
	}

	//Play sound
	if(!m_pickup_sound.empty())
	{
		IEntity *pPicker = m_pEntitySystem->GetEntity(pickerId);
		if(pPicker)
		{
			IEntitySoundProxy* pSoundProxy = (IEntitySoundProxy*)pPicker->GetProxy(ENTITY_PROXY_SOUND);

			if(pSoundProxy)
			{
				//Execute sound at picker position
				pSoundProxy->PlaySound(m_pickup_sound, pPicker->GetWorldPos(),FORWARD_DIRECTION, FLAG_SOUND_DEFAULT_3D, eSoundSemantic_Weapon);
			}
		}
	}

	RemoveEntity();
}
コード例 #12
0
ファイル: GameAudio.cpp プロジェクト: Hellraiser666/CryGame
// add enum of sound you want to play, and add command to game code at appropiate spot
void CGameAudio::PlaySound(EGameAudioSounds eSound, IEntity *pEntity, float param, bool stopSound)
{
	int soundFlag = 0; //localActor will get 2D sounds
	ESoundSemantic eSemantic = eSoundSemantic_None;
	ISound *pSound = NULL;
	bool	setParam = false;
	static string soundName;
	soundName.resize(0);

	switch(eSound)
	{
	case EGameAudioSound_NOSOUND:
		break;

	case EGameAudioSound_LAST:
		break;

	default:
		break;
	}

	//if(!force3DSound && m_pOwner == m_pGameFramework->GetClientActor() && !m_pOwner->IsThirdPerson() && soundName.size())
	//{
	//	if (bAppendPostfix)
	//		soundName.append("_fp");
	//}

	IEntitySoundProxy *pSoundProxy = pEntity ? (IEntitySoundProxy *)pEntity->CreateProxy(ENTITY_PROXY_SOUND) : NULL;

	if(soundName.size() && eSound < EGameAudioSound_LAST)		//get / create or stop sound
	{
		if(m_sounds[eSound].ID != INVALID_SOUNDID)
		{
			if(pSoundProxy)
				pSound = pSoundProxy->GetSound(m_sounds[eSound].ID);
			else
				pSound = gEnv->pSoundSystem->GetSound(m_sounds[eSound].ID);

			if(stopSound)
			{
				if(pSound)
					pSound->Stop();

				m_sounds[eSound].ID = INVALID_SOUNDID;
				return;
			}
		}

		if(!pSound && !stopSound)
		{
			pSound = gEnv->pSoundSystem->CreateSound(soundName, soundFlag);

			if(pSound)
			{
				pSound->SetSemantic(eSemantic);
				//float fTemp = 0.0f;
				m_sounds[eSound].ID = pSound->GetId();
				m_sounds[eSound].bLooping = (pSound->GetFlags() & FLAG_SOUND_LOOP) != 0;
				m_sounds[eSound].b3D = (pSound->GetFlags() & FLAG_SOUND_3D) != 0;
				//m_sounds[eSound].nMassIndex = pSound->GetParam("mass", &fTemp, false);
				//m_sounds[eSound].nSpeedIndex = pSound->GetParam("speed", &fTemp, false);
				//m_sounds[eSound].nStrengthIndex = pSound->GetParam("strength", &fTemp, false);
			}
		}
	}

	if(pSound)		//set params and play
	{
		//pSound->SetPosition(m_pOwner->GetEntity()->GetWorldPos());

		if(setParam)
		{
			//if (m_sounds[eSound].nMassIndex != -1)
			//	pSound->SetParam(m_sounds[sound].nMassIndex, param);

			//if (m_sounds[eSound].nSpeedIndex != -1)
			//	pSound->SetParam(m_sounds[sound].nSpeedIndex, param);

			//if (m_sounds[eSound].nStrengthIndex != -1)
			//	pSound->SetParam(m_sounds[sound].nStrengthIndex, param);
		}

		if(!(m_sounds[eSound].bLooping && pSound->IsPlaying()))
		{
			if(pSoundProxy)
				pSoundProxy->PlaySound(pSound);
			else
				pSound->Play();
		}
	}
}
コード例 #13
0
//------------------------------------------------------------------------
tSoundID CItem::PlayAction(const ItemString &actionName, int layer, bool loop, uint32 flags, float speedOverride)
{
	if(!m_enableAnimations || !IsOwnerInGame())
		return (tSoundID)-1;

	TActionMap::iterator it = m_sharedparams->actions.find(CONST_TEMPITEM_STRING(actionName));

	if(it == m_sharedparams->actions.end())
	{
//		GameWarning("Action '%s' not found on item '%s'!", actionName, GetEntity()->GetName());

		for(int i=0; i<eIGS_Last; i++)
		{
			m_animationTime[i]=0;
			m_animationSpeed[i]=1.0f;
			m_animationEnd[i]=0;
		}

		return 0;
	}

	bool fp = m_stats.fp;

	if(m_parentId)
	{
		CItem *pParent=static_cast<CItem *>(m_pItemSystem->GetItem(m_parentId));

		if(pParent)
			fp=pParent->GetStats().fp;
	}

	if(flags&eIPAF_ForceFirstPerson)
		fp = true;

	if(flags&eIPAF_ForceThirdPerson)
		fp = false;

	int sid=fp?eIGS_FirstPerson:eIGS_ThirdPerson;
	SAction &action = it->second;

	tSoundID result = INVALID_SOUNDID;

	if((flags&eIPAF_Sound) && !action.sound[sid].name.empty() && IsSoundEnabled() && g_pGameCVars->i_soundeffects)
	{
		int nSoundFlags = FLAG_SOUND_DEFAULT_3D;
		nSoundFlags |= flags&eIPAF_SoundStartPaused?FLAG_SOUND_START_PAUSED:0;
		IEntitySoundProxy *pSoundProxy = GetSoundProxy(true);

		//GetSound proxy from dualwield master if neccesary
		if(IsDualWieldSlave())
		{
			CItem *pMaster = static_cast<CItem *>(GetDualWieldMaster());

			if(pMaster)
			{
				pSoundProxy = pMaster->GetSoundProxy(true);
			}
		}

		EntityId pSkipEnts[3];
		int nSkipEnts = 0;

		// TODO for Marcio :)
		// check code changes

		// Skip the Item
		pSkipEnts[nSkipEnts] = GetEntity()->GetId();
		++nSkipEnts;

		// Skip the Owner
		if(GetOwner())
		{
			pSkipEnts[nSkipEnts] = GetOwner()->GetId();
			++nSkipEnts;
		}

		if(pSoundProxy)
		{

			TempResourceName name;
			FixResourceName(action.sound[sid].name, name, flags);
			//nSoundFlags = nSoundFlags | (fp?FLAG_SOUND_DEFAULT_3D|FLAG_SOUND_RELATIVE:FLAG_SOUND_DEFAULT_3D);
			Vec3 vOffset(0,0,0);

			if(fp)
				vOffset.x = 0.3f; // offset for first person weapon to the front

			if(!g_pGameCVars->i_staticfiresounds)
			{
				result = pSoundProxy->PlaySoundEx(name, vOffset, FORWARD_DIRECTION, nSoundFlags, 1.0f, 0, 0, eSoundSemantic_Weapon, pSkipEnts, nSkipEnts);
				ISound *pSound = pSoundProxy->GetSound(result);

				if(pSound && action.sound[sid].sphere>0.0f)
					pSound->SetSphereSpec(action.sound[sid].sphere);
			}
			else
			{
				SInstanceAudio *pInstanceAudio=0;

				if(action.sound[sid].isstatic)
				{
					TInstanceActionMap::iterator iit = m_instanceActions.find(CONST_TEMPITEM_STRING(actionName));

					if(iit == m_instanceActions.end())
					{
						std::pair<TInstanceActionMap::iterator, bool> insertion=m_instanceActions.insert(TInstanceActionMap::value_type(actionName, SInstanceAction()));
						pInstanceAudio=&insertion.first->second.sound[sid];
					}
					else
						pInstanceAudio=&iit->second.sound[sid];
				}

				if(pInstanceAudio && (pInstanceAudio->id != INVALID_SOUNDID) && (name != pInstanceAudio->static_name))
					ReleaseStaticSound(pInstanceAudio);

				if(!pInstanceAudio || pInstanceAudio->id == INVALID_SOUNDID)
				{
					result = pSoundProxy->PlaySoundEx(name, vOffset, FORWARD_DIRECTION, nSoundFlags, 1.0f, 0, 0, eSoundSemantic_Weapon, pSkipEnts, nSkipEnts);
					ISound *pSound = pSoundProxy->GetSound(result);

					if(pSound && action.sound[sid].sphere>0.0f)
						pSound->SetSphereSpec(action.sound[sid].sphere);
				}

				if(action.sound[sid].isstatic)
				{
					if(pInstanceAudio->id == INVALID_SOUNDID)
					{
						if(pSoundProxy->SetStaticSound(result, true))
						{
							pInstanceAudio->id = result;
							pInstanceAudio->static_name = name;
							pInstanceAudio->synch = action.sound[sid].issynched;
						}
					}
					else
					{
						ISound *pSound = pSoundProxy->GetSound(pInstanceAudio->id);

						if(pSound)
							pSound->Play(1.0, true, true, pSoundProxy);
					}
				}
			}

			if(gEnv->pAISystem && action.sound[sid].airadius > 0.0f)
			{
				EntityId ownerId = GetOwner() ? GetOwner()->GetId() : 0;

				// associate sound event with vehicle if the shooter is in a vehicle (tank cannon shot, etc)
				if(CActor *pOwnerActor = GetOwnerActor())
				{
					IVehicle *pOwnerVehicle = pOwnerActor->GetLinkedVehicle();

					if(pOwnerVehicle && pOwnerVehicle->GetEntityId())
						ownerId = pOwnerVehicle->GetEntityId();
				}

				SAIStimulus stim(AISTIM_SOUND, AISOUND_WEAPON, ownerId?ownerId:GetEntityId() , 0,
								 GetEntity()->GetWorldPos(), ZERO, action.sound[sid].airadius);

				gEnv->pAISystem->RegisterStimulus(stim);
			}
		}
	}


	if(flags&eIPAF_Animation)
	{
		TempResourceName name;
		// generate random number only once per call to allow animations to
		// match across geometry slots (like first person and third person)
		float randomNumber = Random();

		for(int i=0; i<eIGS_Last; i++)
		{
			if(!(flags&(1<<i)))
				continue;

			int nanimations=action.animation[i].size();

			if(nanimations <= 0)
				continue;

			int anim = int(randomNumber * float(nanimations));

			if(action.animation[i][anim].name.empty())
				continue;

			FixResourceName(action.animation[i][anim].name, name, flags);

			if((i == eIGS_Owner) || (i == eIGS_OwnerLooped))
			{
				if(!action.animation[i][anim].name.empty())
				{
					bool looping=(eIGS_OwnerLooped==i);

					CActor *pOwner = GetOwnerActor();

					if(pOwner)
					{
						if(IsDualWield() && !m_sharedparams->params.dual_wield_pose.empty())
							pOwner->PlayAction(name, m_sharedparams->params.dual_wield_pose.c_str(), looping);
						else
							pOwner->PlayAction(name, m_sharedparams->params.pose.c_str(), looping);
					}
				}

				continue;
			}
			else if(i == eIGS_OffHand)
			{
				if(!action.animation[eIGS_OffHand][anim].name.empty())
				{
					CActor *pOwner = GetOwnerActor();

					if(pOwner)
					{
						CItem *pOffHand = pOwner->GetItemByClass(CItem::sOffHandClass);

						if(pOffHand && pOffHand!=this)
						{
							uint32 ohflags=eIPAF_Default;

							if(action.animation[eIGS_OffHand][anim].blend==0.0f)
								ohflags|=eIPAF_NoBlend;

							pOffHand->PlayAction(action.animation[eIGS_OffHand][anim].name, 0, false, ohflags);
						}
					}
				}

				continue;
			}

			SAnimation &animation=action.animation[i][anim];

			if(!animation.name.empty())
			{
				float blend = animation.blend;

				if(flags&eIPAF_NoBlend)
					blend = 0.0f;

				if(speedOverride > 0.0f)
					PlayAnimationEx(name, i, layer, loop, blend, speedOverride, flags);
				else
					PlayAnimationEx(name, i, layer, loop, blend, animation.speed, flags);
			}

			if((m_stats.fp || m_stats.viewmode&eIVM_FirstPerson) && i==eIGS_FirstPerson && !animation.camera_helper.empty())
			{
				m_camerastats.animating=true;
				m_camerastats.helper=animation.camera_helper;
				m_camerastats.position=animation.camera_pos;
				m_camerastats.rotation=animation.camera_rot;
				m_camerastats.follow=animation.camera_follow;
				m_camerastats.reorient=animation.camera_reorient;
			}
			else if(m_camerastats.animating)
				m_camerastats=SCameraAnimationStats();
		}
	}

	if(flags&eIPAF_Effect && !action.effect[sid].name.empty())
	{
		// change this to attach, if needed
		SpawnEffect(sid, action.effect[sid].name.c_str(), action.effect[sid].helper.c_str());
	}

	if(action.children)
	{
		for(TAccessoryMap::iterator ait=m_accessories.begin(); ait!=m_accessories.end(); ait++)
		{
			EntityId aId=(EntityId)ait->second;
			CItem *pAccessory=static_cast<CItem *>(m_pItemSystem->GetItem(aId));

			if(pAccessory)
				pAccessory->PlayAction(actionName, layer, loop, flags, speedOverride);
		}
	}

	return result;
}
コード例 #14
0
//------------------------------------------------------------------------
void CItem::FixResourceName(const ItemString &inName, TempResourceName &name, int flags, const char *hand, const char *suffix, const char *pose, const char *pov, const char *env)
{
	// the whole thing of fixing is not nice, but at least we don't allocate too often
	// StringHelper<TempResourceName::SIZE> name (inName.c_str(), inName.length());
	name.assign(inName.c_str(), inName.length());

	if(!hand)
	{
		if(m_stats.hand == eIH_Left)
			hand = "left";
		else
			hand = "right";
	}

	name.replace("%hand%", hand);

	if(m_stats.hand == eIH_Left)
		name.replace("%offhand%", "right");
	else
		name.replace("%offhand%", "left");

	if(!suffix)
		suffix = m_actionSuffix.c_str();

	name.replace("%suffix%", suffix);

	if(!pose)
	{
		if(!m_sharedparams->params.pose.empty())
			pose = m_sharedparams->params.pose.c_str();
		else
			pose = "";
	}

	name.replace("%pose%", "");

	if(!pov)
	{
		if((m_stats.fp || flags&eIPAF_ForceFirstPerson) && !(flags&eIPAF_ForceThirdPerson))
			pov = ITEM_FIRST_PERSON_TOKEN;
		else
			pov = ITEM_THIRD_PERSON_TOKEN;
	}

	name.replace("%pov%", pov);

	if(!env)
	{
		// Instead if the weapons sound proxy, the owners is used to retrieve the tail name
		IEntity *pOwner = GetOwner();

		if(GetIWeapon() && pOwner)  // restricting to weapon sounds only
		{
			if(pOwner)
			{
				IEntitySoundProxy *pSoundProxy = (IEntitySoundProxy *)pOwner->GetProxy(ENTITY_PROXY_SOUND);

				if(!pSoundProxy)
					pSoundProxy = (IEntitySoundProxy *)pOwner->CreateProxy(ENTITY_PROXY_SOUND);

				if(pSoundProxy)
				{
					// check for a roof 10m above the Owner
					// recalculate visibility when owner move more than 2 meters
					pSoundProxy->CheckVisibilityForTailName(10.0f, 2.0f);
					env = pSoundProxy->GetTailName();
				}
			}
		}


		if(!env || !env[0] || !stricmp("indoor", env))
			name.replace("%env%", "");
		else
		{
			static const size_t MAX_LEN = 256;
			char envstr[MAX_LEN];
			envstr[0] = '_';
			strncpy(envstr+1, env, MAX_LEN-1); // no 0 pad, if MAX_LEN-1 are copied
			envstr[MAX_LEN-1] = '\0'; // always zero-terminate
			name.replace("%env%", envstr);
		}
	}
	else
		name.replace("%env%", env);
}