 MultipleUnitSounds.patch
 MultipleUnitSounds.patch (31,866 bytes) 
2007-05-29 22:31  
Index: Lua/LuaUnitDefs.cpp
===================================================================
--- Lua/LuaUnitDefs.cpp	(revision 3795)
+++ Lua/LuaUnitDefs.cpp	(working copy)
@@ -462,23 +462,27 @@
 }
 
 
-static void PushGuiSound(lua_State* L,
-                         const string& name, const GuiSound& sound)
-{
+static void PushGuiSound(lua_State* L, const string& name, const GuiSound& sound) {
 	lua_pushstring(L, name.c_str());
 	lua_newtable(L);
-	HSTR_PUSH_STRING(L, "name",   sound.name);
+
+	for (int idx = 0; idx < sound.names.size(); idx++)
+		HSTR_PUSH_STRING(L, "name", ((GuiSound&) sound).getName(idx));
+
 	if (CLuaHandle::GetActiveHandle()->GetUserMode()) {
-		HSTR_PUSH_NUMBER(L, "id",   sound.id); 
+		for (int idx = 0; idx < sound.ids.size(); idx++)
+			HSTR_PUSH_NUMBER(L, "id", ((GuiSound&) sound).getID(idx));
 	}
-	HSTR_PUSH_NUMBER(L, "volume", sound.volume);
-	lua_rawset(L, -3);  
+
+	for (int idx = 0; idx < sound.volumes.size(); idx++)
+		HSTR_PUSH_NUMBER(L, "volume", ((GuiSound&) sound).getVolume(idx));
+
+	lua_rawset(L, -3);
 }
 
 
-static int SoundsTable(lua_State* L, const void* data)
-{
-	const UnitDef::SoundStruct& sounds = *((const UnitDef::SoundStruct*)data);
+static int SoundsTable(lua_State* L, const void* data) {
+	const UnitDef::SoundStruct& sounds = *((const UnitDef::SoundStruct*) data);
 
 	lua_newtable(L);
 	PushGuiSound(L, "select",      sounds.select);
@@ -496,9 +500,10 @@
 }
 
 
-static int ModelDefTable(lua_State* L, const void* data)
-{
-	const UnitModelDef& md = *((const UnitModelDef*)data);
+
+
+static int ModelDefTable(lua_State* L, const void* data) {
+	const UnitModelDef& md = *((const UnitModelDef*) data);
 	lua_newtable(L);
 	HSTR_PUSH_STRING(L, "path", md.modelpath);
 	HSTR_PUSH_STRING(L, "name", md.modelname);
Index: Lua/LuaWeaponDefs.cpp
===================================================================
--- Lua/LuaWeaponDefs.cpp	(revision 3795)
+++ Lua/LuaWeaponDefs.cpp	(working copy)
@@ -397,15 +397,17 @@
 }
 
 
+
 static int GuiSoundTable(lua_State* L, const void* data)
 {
-	const GuiSound& sound = *((const GuiSound*)data);
+	const GuiSound& sound = *((const GuiSound*) data);
 	lua_newtable(L);
-	HSTR_PUSH_STRING(L, "name",   sound.name);
+
+	HSTR_PUSH_STRING(L, "name", ((GuiSound&) sound).getName(0));
 	if (CLuaHandle::GetActiveHandle()->GetUserMode()) {
-		HSTR_PUSH_NUMBER(L, "id",   sound.id);
+		HSTR_PUSH_NUMBER(L, "id", ((GuiSound&) sound).getID(0));
 	}
-	HSTR_PUSH_NUMBER(L, "volume", sound.volume);
+	HSTR_PUSH_NUMBER(L, "volume", ((GuiSound&) sound).getVolume(0));
 	return 1;
 }
 
Index: Sim/Units/Unit.cpp
===================================================================
--- Sim/Units/Unit.cpp	(revision 3795)
+++ Sim/Units/Unit.cpp	(working copy)
@@ -758,11 +758,18 @@
 		    && (team == gu->myTeam) && !camera->InView(midPos,radius+50) && !gu->spectatingFullView) {
 			logOutput.Print("%s is being attacked",unitDef->humanName.c_str());
 			logOutput.SetLastMsgPos(pos);
-			if (unitDef->isCommander || uh->lastDamageWarning+150<gs->frameNum) {
-				sound->PlaySample(unitDef->sounds.underattack.id,unitDef->isCommander?4:2);
+
+			if (unitDef->isCommander || uh->lastDamageWarning + 150 < gs->frameNum) {
+				int soundIdx = unitDef->sounds.underattack.getRandomIdx();
+				if (soundIdx >= 0) {
+					sound->PlaySample(
+						unitDef->sounds.underattack.getID(soundIdx),
+						unitDef->isCommander? 4: 2);
+				}
 			}
-			minimap->AddNotification(pos,float3(1,0.3f,0.3f),unitDef->isCommander?1:0.5f);	//todo: make compatible with new gui
 
+			minimap->AddNotification(pos,float3(1,0.3f,0.3f),unitDef->isCommander? 1: 0.5f);	//todo: make compatible with new gui
+
 			uh->lastDamageWarning=gs->frameNum;
 			if(unitDef->isCommander)
 				uh->lastCmdDamageWarning=gs->frameNum;
@@ -1512,42 +1519,42 @@
 		gs->Team(team)->CommanderDied(this);
 	gs->Team(this->lineage)->LeftLineage(this);
 
-	if(!reclaimed && !beingBuilt){
+	if (!reclaimed && !beingBuilt) {
 		string exp;
-		if(selfDestruct)
-			exp=unitDef->selfDExplosion;
+		if (selfDestruct)
+			exp = unitDef->selfDExplosion;
 		else
-			exp=unitDef->deathExplosion;
+			exp = unitDef->deathExplosion;
 
-		if(!exp.empty()){
-			WeaponDef* wd=weaponDefHandler->GetWeapon(exp);
-			if(wd){
-				helper->Explosion(midPos,wd->damages,wd->areaOfEffect,wd->edgeEffectiveness,wd->explosionSpeed,this,true,wd->damages[0]>500?1:2,false,wd->explosionGenerator,0, ZeroVector, wd->id);
+		if (!exp.empty()) {
+			WeaponDef* wd = weaponDefHandler->GetWeapon(exp);
+			if (wd) {
+				helper->Explosion(
+					midPos, wd->damages, wd->areaOfEffect, wd->edgeEffectiveness,
+					wd->explosionSpeed, this, true, wd->damages[0] > 500? 1: 2,
+					false, wd->explosionGenerator, 0, ZeroVector, wd->id
+				);
 
-				// Play explosion sound
-				CWeaponDefHandler::LoadSound(wd->soundhit);
-				if (wd->soundhit.id) {
-
-					// HACK loading code doesn't set sane defaults for explosion sounds, so we do it here
-					if (wd->soundhit.volume == -1)
-						wd->soundhit.volume = 5.0f;
-
-					sound->PlaySample(wd->soundhit.id, pos, wd->soundhit.volume);
+				// play explosion sound
+				if (wd->soundhit.getID(0) > 0) {
+					// HACK: loading code doesn't set sane defaults for explosion sounds, so we do it here
+					// NOTE: actually no longer true, loading code always ensures that sound volume != -1
+					float volume = wd->soundhit.getVolume(0);
+					sound->PlaySample(wd->soundhit.getID(0), pos, (volume == -1)? 5.0f: volume);
 				}
-
-				//logOutput.Print("Should play %s (%d)", wd->soundhit.name.c_str(), wd->soundhit.id);
 			}
 		}
-		if(selfDestruct)
-			recentDamage+=maxHealth*2;
 
+		if (selfDestruct)
+			recentDamage += maxHealth * 2;
+
 		vector<int> args;
-		args.push_back((int)(recentDamage/maxHealth*100));
+		args.push_back((int) (recentDamage / maxHealth * 100));
 		args.push_back(0);
 		cob->Call(COBFN_Killed, args, &CUnitKilledCB, this, NULL);
 
 		UnBlock();
-		delayedWreckLevel=args[1];
+		delayedWreckLevel = args[1];
 //		featureHandler->CreateWreckage(pos,wreckName, heading, args[1],-1,true);
 	} else {
 		deathScriptFinished=true;
@@ -1615,38 +1622,46 @@
 	//if(unitDef->tidalGenerator>0)
 	//	cob->Call(COBFN_SetSpeed, (int)(readmap->tidalStrength*3000.0f*unitDef->tidalGenerator));
 
-	if(activated)
+	if (activated)
 		return;
+
 	activated = true;
-
 	cob->Call(COBFN_Activate);
 
-	if(unitDef->targfac){
-		radarhandler->radarErrorSize[allyteam]/=radarhandler->targFacEffect;
+	if (unitDef->targfac){
+		radarhandler->radarErrorSize[allyteam] /= radarhandler->targFacEffect;
 	}
-	if(hasRadarCapacity)
+	if (hasRadarCapacity)
 		radarhandler->MoveUnit(this);
 
-	if(unitDef->sounds.activate.id)
-		sound->PlayUnitActivate(unitDef->sounds.activate.id, this, unitDef->sounds.activate.volume);
+	int soundIdx = unitDef->sounds.activate.getRandomIdx();
+	if (soundIdx >= 0) {
+		sound->PlayUnitActivate(
+			unitDef->sounds.activate.getID(soundIdx), this,
+			unitDef->sounds.activate.getVolume(soundIdx));
+	}
 }
 
 void CUnit::Deactivate()
 {
-	if(!activated)
+	if (!activated)
 		return;
+
 	activated = false;
-
 	cob->Call(COBFN_Deactivate);
 
-	if(unitDef->targfac){
-		radarhandler->radarErrorSize[allyteam]*=radarhandler->targFacEffect;
+	if (unitDef->targfac){
+		radarhandler->radarErrorSize[allyteam] *= radarhandler->targFacEffect;
 	}
-	if(hasRadarCapacity)
+	if (hasRadarCapacity)
 		radarhandler->RemoveUnit(this);
 
-	if(unitDef->sounds.deactivate.id)
-		sound->PlayUnitActivate(unitDef->sounds.deactivate.id, this, unitDef->sounds.deactivate.volume);
+	int soundIdx = unitDef->sounds.deactivate.getRandomIdx();
+	if (soundIdx >= 0) {
+		sound->PlayUnitActivate(
+			unitDef->sounds.deactivate.getID(soundIdx), this,
+			unitDef->sounds.deactivate.getVolume(soundIdx));
+	}
 }
 
 void CUnit::PushWind(float x, float z, float strength)
Index: Sim/Units/UnitDefHandler.h
===================================================================
--- Sim/Units/UnitDefHandler.h	(revision 3795)
+++ Sim/Units/UnitDefHandler.h	(working copy)
@@ -52,7 +52,8 @@
 
 	void AssignTechLevel(UnitDef& ud, int level);
 	
-	void LoadSound(TdfParser &sunparser, GuiSound &gsound, std::string sunname);
+	void LoadSounds(TdfParser&, GuiSound&, std::string, int);
+	void LoadSound(TdfParser&, GuiSound&, std::string, int);
 
 public:
 //	void CreateBlockingLevels(UnitDef *def,std::string yardmap);
Index: Sim/Units/COB/CobInstance.cpp
===================================================================
--- Sim/Units/COB/CobInstance.cpp	(revision 3795)
+++ Sim/Units/COB/CobInstance.cpp	(working copy)
@@ -629,7 +629,7 @@
 				dir.Normalize();
 				unit->unitDef->sfxExplGens[type-1024]->Explosion(pos, 1, 1, unit, 0, 0, dir);
 			}
-			else if(type&2048)  //make a weapon fire from the piece
+			else if (type&2048)  //make a weapon fire from the piece
 			{
 				//this is very hackish and probably has a lot of side effects, but might be usefull for something
 				//float3 relDir =-unit->localmodel->GetPieceDirection(piece);
@@ -648,12 +648,18 @@
 				unit->weapons[type-2048]->weaponPos = weaponPos;
 
 			}
-			else if(type&4096)  //detonate weapon from piece
-			{
-				WeaponDef *weaponDef = unit->weapons[type-4096]->weaponDef;
-				sound->PlaySample(weaponDef->soundhit.id,unit,weaponDef->soundhit.volume);
-				helper->Explosion(pos,weaponDef->damages,weaponDef->areaOfEffect,weaponDef->edgeEffectiveness,weaponDef->explosionSpeed,unit, true, 1.0f, false,weaponDef->explosionGenerator,NULL,float3(0,0,0), weaponDef->id);
+			else if (type & 4096) {
+				// detonate weapon from piece
+				WeaponDef* weaponDef = unit->weapons[type - 4096]->weaponDef;
+				if (weaponDef->soundhit.getID(0) > 0) {
+					sound->PlaySample(weaponDef->soundhit.getID(0), unit, weaponDef->soundhit.getVolume(0));
+				}
 
+				helper->Explosion(
+					pos, weaponDef->damages, weaponDef->areaOfEffect, weaponDef->edgeEffectiveness,
+					weaponDef->explosionSpeed, unit, true, 1.0f, false, weaponDef->explosionGenerator,
+					NULL, float3(0, 0, 0), weaponDef->id
+				);
 			}
 			break;
 	}
Index: Sim/Units/UnitTypes/Builder.cpp
===================================================================
--- Sim/Units/UnitTypes/Builder.cpp	(revision 3795)
+++ Sim/Units/UnitTypes/Builder.cpp	(working copy)
@@ -573,10 +573,14 @@
 
 	std::vector<int> args;
 	args.push_back(short(h-heading));
-	cob->Call("StartBuilding",args);
+	cob->Call("StartBuilding", args);
 
-	if(unitDef->sounds.build.id)
-		sound->PlaySample(unitDef->sounds.build.id, pos, unitDef->sounds.build.volume);
+	int soundIdx = unitDef->sounds.build.getRandomIdx();
+	if (soundIdx >= 0) {
+		sound->PlaySample(
+			unitDef->sounds.build.getID(soundIdx), pos,
+			unitDef->sounds.build.getVolume(soundIdx));
+	}
 }
 
 
Index: Sim/Units/UnitTypes/Factory.cpp
===================================================================
--- Sim/Units/UnitTypes/Factory.cpp	(revision 3795)
+++ Sim/Units/UnitTypes/Factory.cpp	(working copy)
@@ -105,11 +105,14 @@
 
 			cob->Call("StartBuilding");
 
-			if (unitDef->sounds.build.id) {
-				sound->PlaySample(unitDef->sounds.build.id, pos, unitDef->sounds.build.volume);
+			int soundIdx = unitDef->sounds.build.getRandomIdx();
+			if (soundIdx >= 0) {
+				sound->PlaySample(
+					unitDef->sounds.build.getID(soundIdx), pos,
+					unitDef->sounds.build.getVolume(0));
 			}
 		} else {
-			helper->BuggerOff(buildPos-float3(0.01f,0,0.02f),radius+8);
+			helper->BuggerOff(buildPos - float3(0.01f, 0, 0.02f), radius + 8);
 		}
 	}
 
Index: Sim/Units/UnitDef.h
===================================================================
--- Sim/Units/UnitDef.h	(revision 3795)
+++ Sim/Units/UnitDef.h	(working copy)
@@ -12,15 +12,61 @@
 struct WeaponDef;
 class CExplosionGenerator;
 
-const int MAX_UNITS=5000;
+const int MAX_UNITS = 5000;
 
+
 struct GuiSound
 {
-	std::string name;
-	int id;
-	float volume;
+	// note: vector sizes are always equal
+	// (vector of triples would be better)
+	std::vector<std::string> names;
+	std::vector<int> ids;
+	std::vector<float> volumes;
+
+	// return a random sound index if more than one sound was loaded
+	// (used for unit acknowledgements, could be called for weapons too)
+	int getRandomIdx(void) {
+		switch (ids.size()) {
+			case 0: { return -1; } break;
+			case 1: { return 0; } break;
+			default: {
+				return int((float(rand()) / RAND_MAX) * ids.size());
+			} break;
+		}
+	}
+
+	// get a (loaded) sound's name for index <idx>
+	std::string getName(int idx) {
+		return (idx >= 0 && idx < names.size())? names[idx]: "";
+	}
+	// get a (loaded) sound's ID for index <idx>
+	int getID(int idx) {
+		return (idx >= 0 && idx < ids.size())? ids[idx]: 0;
+	}
+	// get a (loaded) sound's volume for index <idx>
+	float getVolume(int idx) {
+		return (idx >= 0 && idx < volumes.size())? volumes[idx]: 0.0f;
+	}
+
+	// set a (loaded) sound's name for index <idx>
+	void setName(int idx, std::string name) {
+		if (idx >= 0 && idx < names.size())
+			names[idx] = name;
+	}
+	// set a (loaded) sound's ID for index <idx>
+	void setID(int idx, int ID) {
+		if (idx >= 0 && idx < ids.size())
+			ids[idx] = ID;
+	}
+	// set a (loaded) sound's volume for index <idx>
+	void setVolume(int idx, float volume) {
+		if (idx >= 0 && idx < volumes.size())
+			volumes[idx] = volume;
+	}
 };
 
+
+
 struct UnitModelDef
 {
 	std::string modelpath;
Index: Sim/Units/UnitLoader.cpp
===================================================================
--- Sim/Units/UnitLoader.cpp	(revision 3795)
+++ Sim/Units/UnitLoader.cpp	(working copy)
@@ -409,42 +409,46 @@
 	weapon->metalFireCost=weapondef->metalcost;
 	weapon->energyFireCost=weapondef->energycost;
 
-	CWeaponDefHandler::LoadSound(weapondef->firesound);
-	CWeaponDefHandler::LoadSound(weapondef->soundhit);
 
-	if(weapondef->firesound.volume == -1 || weapondef->soundhit.volume == -1)  //no volume read from defenition
-	{
-		if(weapon->damages[0]>50){
-			float soundVolume=sqrt(weapon->damages[0]*0.5f);
-			if(weapondef->type=="LaserCannon")
-				soundVolume*=0.5f;
-			float hitSoundVolume=soundVolume;
-			if((weapondef->type=="MissileLauncher" || weapondef->type=="StarburstLauncher") && soundVolume>100)
-				soundVolume=10*sqrt(soundVolume);
-			if(weapondef->firesound.volume==-1)
-				weapondef->firesound.volume=soundVolume;
-			soundVolume=hitSoundVolume;
-			if(weapon->areaOfEffect>8)
-				soundVolume*=2;
-			if(weapondef->type=="DGun")
-				soundVolume*=0.15f;
-			if(weapondef->soundhit.volume==-1)
-				weapondef->soundhit.volume=soundVolume;
+	if (weapondef->firesound.getVolume(0) == -1 || weapondef->soundhit.getVolume(0) == -1) {
+		// no volume (-1) read from weapon definition, set it dynamically here
+		if (weapon->damages[0] > 50) {
+			float soundVolume = sqrt(weapon->damages[0] * 0.5f);
+
+			if (weapondef->type == "LaserCannon")
+				soundVolume *= 0.5f;
+
+			float hitSoundVolume = soundVolume;
+
+			if ((weapondef->type == "MissileLauncher" || weapondef->type == "StarburstLauncher") && soundVolume > 100)
+				soundVolume = 10 * sqrt(soundVolume);
+			if (weapondef->firesound.getVolume(0) == -1)
+				weapondef->firesound.setVolume(0, soundVolume);
+
+			soundVolume = hitSoundVolume;
+
+			if (weapon->areaOfEffect > 8)
+				soundVolume *= 2;
+			if (weapondef->type == "DGun")
+				soundVolume *= 0.15f;
+			if (weapondef->soundhit.getVolume(0) == -1)
+				weapondef->soundhit.setVolume(0, soundVolume);
 		}
-		else
-		{
-			weapondef->soundhit.volume = 5.0f;
-			weapondef->firesound.volume = 5.0f;
+		else {
+			weapondef->soundhit.setVolume(0, 5.0f);
+			weapondef->firesound.setVolume(0, 5.0f);
 		}
 	}
-	weapon->fireSoundId = weapondef->firesound.id;
-	weapon->fireSoundVolume=weapondef->firesound.volume;
 
-	weapon->onlyForward=weapondef->onlyForward;
-	if(owner->unitDef->type=="Fighter" && !owner->unitDef->hoverAttack)		//fighter aircrafts have too big tolerance in ta
-		weapon->maxAngleDif=cos(weapondef->maxAngle*0.4f/180*PI);
+	weapon->fireSoundId = weapondef->firesound.getID(0);
+	weapon->fireSoundVolume = weapondef->firesound.getVolume(0);
+
+
+	weapon->onlyForward = weapondef->onlyForward;
+	if (owner->unitDef->type == "Fighter" && !owner->unitDef->hoverAttack)	// fighter aircraft have too big tolerance in TA
+		weapon->maxAngleDif = cos(weapondef->maxAngle * 0.4f / 180 * PI);
 	else
-		weapon->maxAngleDif=cos(weapondef->maxAngle/180*PI);
+		weapon->maxAngleDif = cos(weapondef->maxAngle / 180 * PI);
 
 	weapon->weaponNum=owner->weapons.size();
 
Index: Sim/Units/UnitDefHandler.cpp
===================================================================
--- Sim/Units/UnitDefHandler.cpp	(revision 3795)
+++ Sim/Units/UnitDefHandler.cpp	(working copy)
@@ -716,39 +716,66 @@
 		ud.customParams = tdfparser.GetAllValues("UNITINFO\\CustomParams");
 	}
 
-	LoadSound(tdfparser, ud.sounds.ok, "ok1");
-	LoadSound(tdfparser, ud.sounds.select, "select1");
-	LoadSound(tdfparser, ud.sounds.arrived, "arrived1");
-	LoadSound(tdfparser, ud.sounds.build, "build");
-	LoadSound(tdfparser, ud.sounds.activate, "activate");
-	LoadSound(tdfparser, ud.sounds.deactivate, "deactivate");
-	LoadSound(tdfparser, ud.sounds.cant, "cant");
-	LoadSound(tdfparser, ud.sounds.underattack, "underattack");
+	LoadSounds(tdfparser, ud.sounds.ok, "ok", 8);					// eg. "ok1", "ok2", ...
+	LoadSounds(tdfparser, ud.sounds.select, "select", 8);			// eg. "select1", "select2", ...
+	LoadSounds(tdfparser, ud.sounds.arrived, "arrived", 8);			// eg. "arrived1", "arrived2", ...
+	LoadSounds(tdfparser, ud.sounds.build, "build", 1);
+	LoadSounds(tdfparser, ud.sounds.activate, "activate", 1);
+	LoadSounds(tdfparser, ud.sounds.deactivate, "deactivate", 1);
+	LoadSounds(tdfparser, ud.sounds.cant, "cant", 1);
+	LoadSounds(tdfparser, ud.sounds.underattack, "underattack", 1);
 }
 
 
-void CUnitDefHandler::LoadSound(TdfParser &tdfparser, GuiSound &gsound, std::string sunname)
+
+void CUnitDefHandler::LoadSounds(TdfParser &tdfparser, GuiSound& gsound, std::string soundName, int numSounds)
 {
-	soundcategory.GetDef(gsound.name, "", tdfparser.SGetValueDef("", "UNITINFO\\SoundCategory")+"\\"+sunname);
-	if(gsound.name.compare("")==0)
-		gsound.id = 0;
-	else
-	{
-		const string soundFile = "sounds/" + gsound.name + ".wav";
+	if (numSounds > 1) {
+		for (int i = 1; i <= numSounds; i++) {
+			// soundnames are 1-based (eg. "ok1", "ok2", ...)
+			// if more than one is available, so start at 1
+			LoadSound(tdfparser, gsound, soundName, i);
+		}
+	} else {
+		LoadSound(tdfparser, gsound, soundName, 0);
+	}
+}
+
+void CUnitDefHandler::LoadSound(TdfParser &tdfparser, GuiSound& gsound, std::string soundCatName, int soundNum)
+{
+	if (soundNum > 0) {
+		// soundNum of 0 means this sound has no
+		// alternates (ie. no number suffix)
+		char buf[8];
+		sprintf(buf, "%d", soundNum);
+		soundCatName += buf;
+	}
+
+	string soundName = "";
+
+	// extract the sound's actual name sans extension (eg. "kbarmsel")
+	soundcategory.GetDef(soundName, "", tdfparser.SGetValueDef("", "UNITINFO\\SoundCategory") + "\\" + soundCatName);
+
+	if (soundName.compare("") != 0) {
+		const string soundFile = "sounds/" + soundName + ".wav";
 		CFileHandler file(soundFile);
-		if(file.FileExists()) {
+
+		if (file.FileExists()) {
+			// we have a valid soundfile: store name, ID, and default volume
 			PUSH_CODE_MODE;
 			ENTER_UNSYNCED;
 			int id = sound->GetWaveId(soundFile);
 			POP_CODE_MODE;
-			gsound.id = id;
-		} else
-			gsound.id = 0;
+
+			gsound.names.push_back(soundName);
+			gsound.ids.push_back(id);
+			gsound.volumes.push_back(5.0f);
+		}
 	}
-	gsound.volume = 5.0f;
 }
 
 
+
 void CUnitDefHandler::ParseUnit(std::string file, int id)
 {
   try {
Index: Sim/Projectiles/WeaponProjectile.cpp
===================================================================
--- Sim/Projectiles/WeaponProjectile.cpp	(revision 3795)
+++ Sim/Projectiles/WeaponProjectile.cpp	(working copy)
@@ -117,15 +117,15 @@
 		
 		helper->Explosion(pos,weaponDef->dynDamageExp>0?dynDamages:weaponDef->damages,weaponDef->areaOfEffect,weaponDef->edgeEffectiveness,weaponDef->explosionSpeed,owner,true,weaponDef->noExplode? 0.3f:1,weaponDef->noExplode || weaponDef->noSelfDamage, weaponDef->explosionGenerator,0,impactDir, weaponDef->id);
 	}
-		
-	if(weaponDef->soundhit.id)
-		sound->PlaySample(weaponDef->soundhit.id,this,weaponDef->soundhit.volume);
 
-	if(!weaponDef->noExplode)
+	if (weaponDef->soundhit.getID(0) > 0) {
+		sound->PlaySample(weaponDef->soundhit.getID(0), this, weaponDef->soundhit.getVolume(0));
+	}
+
+	if (!weaponDef->noExplode)
 		CProjectile::Collision();
-	else
-	{
-		if(TraveledRange())
+	else {
+		if (TraveledRange())
 			CProjectile::Collision();
 	}
 
@@ -141,8 +141,7 @@
 
 void CWeaponProjectile::Collision(CUnit* unit)
 {
-	if(!weaponDef->noExplode || gs->frameNum&1)
-	{
+	if (!weaponDef->noExplode || gs->frameNum & 1) {
 		float3 impactDir = speed;
 		impactDir.Normalize();
 
@@ -154,14 +153,14 @@
 		helper->Explosion(pos,weaponDef->dynDamageExp>0?dynDamages:weaponDef->damages,weaponDef->areaOfEffect,weaponDef->edgeEffectiveness,weaponDef->explosionSpeed,owner,true,weaponDef->noExplode? 0.3f:1,weaponDef->noExplode,weaponDef->explosionGenerator,unit,impactDir, weaponDef->id);
 	}
 
-	if(weaponDef->soundhit.id)
-		sound->PlaySample(weaponDef->soundhit.id,this,weaponDef->soundhit.volume);
+	if (weaponDef->soundhit.getID(0) > 0) {
+		sound->PlaySample(weaponDef->soundhit.getID(0), this, weaponDef->soundhit.getVolume(0));
+	}
 
 	if(!weaponDef->noExplode)
 		CProjectile::Collision(unit);
-	else
-	{
-		if(TraveledRange())
+	else {
+		if (TraveledRange())
 			CProjectile::Collision();
 	}
 }
Index: Sim/MoveTypes/groundmovetype.cpp
===================================================================
--- Sim/MoveTypes/groundmovetype.cpp	(revision 3795)
+++ Sim/MoveTypes/groundmovetype.cpp	(working copy)
@@ -428,10 +428,14 @@
 	StartEngine();
 
 	ENTER_UNSYNCED;
-	if(owner->team == gu->myTeam){
-		//Play "activate" sound.
-		if(owner->unitDef->sounds.activate.id)
-			sound->PlayUnitActivate(owner->unitDef->sounds.activate.id, owner, owner->unitDef->sounds.activate.volume);
+	if (owner->team == gu->myTeam) {
+		// Play "activate" sound.
+		int soundIdx = owner->unitDef->sounds.activate.getRandomIdx();
+		if (soundIdx >= 0) {
+			sound->PlayUnitActivate(
+				owner->unitDef->sounds.activate.getID(soundIdx), owner,
+				owner->unitDef->sounds.activate.getVolume(soundIdx));
+		}
 	}
 	ENTER_SYNCED;
 }
@@ -1178,11 +1182,15 @@
 
 		StopEngine();
 
-		//Play "arrived" sound.
+		// Play "arrived" sound.
 		ENTER_UNSYNCED;
-		if(owner->team == gu->myTeam){
-			if(owner->unitDef->sounds.arrived.id)
-				sound->PlayUnitReply(owner->unitDef->sounds.arrived.id, owner, owner->unitDef->sounds.arrived.volume);
+		if (owner->team == gu->myTeam) {
+			int soundIdx = owner->unitDef->sounds.arrived.getRandomIdx();
+			if (soundIdx >= 0) {
+				sound->PlayUnitReply(
+					owner->unitDef->sounds.arrived.getID(soundIdx), owner,
+					owner->unitDef->sounds.arrived.getVolume(soundIdx));
+			}
 		}
 		ENTER_SYNCED;
 
@@ -1214,12 +1222,16 @@
 
 	//Sends a message to user.
 	ENTER_UNSYNCED;
-	if(owner->team == gu->myTeam){
-		//Playing "can't" sound.
-		if(owner->unitDef->sounds.cant.id)
-			sound->PlayUnitReply(owner->unitDef->sounds.cant.id, owner, owner->unitDef->sounds.cant.volume);
+	if (owner->team == gu->myTeam) {
+		// Playing "can't" sound.
+		int soundIdx = owner->unitDef->sounds.cant.getRandomIdx();
+		if (soundIdx >= 0) {
+			sound->PlayUnitReply(
+				owner->unitDef->sounds.cant.getID(soundIdx), owner,
+				owner->unitDef->sounds.cant.getVolume(soundIdx));
+		}
 
-		if(owner->pos.distance(goal)>goalRadius+150){
+		if (owner->pos.distance(goal) > goalRadius + 150) {
 			logOutput << owner->unitDef->humanName.c_str() << ": Can't reach destination!\n";
 			logOutput.SetLastMsgPos(owner->pos);
 		}
Index: Sim/Weapons/WeaponDefHandler.cpp
===================================================================
--- Sim/Weapons/WeaponDefHandler.cpp	(revision 3795)
+++ Sim/Weapons/WeaponDefHandler.cpp	(working copy)
@@ -56,7 +56,10 @@
 	delete[] weaponDefs;
 }
 
-void CWeaponDefHandler::ParseTAWeapon(TdfParser *sunparser, std::string weaponname, int id)
+
+
+
+void CWeaponDefHandler::ParseTAWeapon(TdfParser* sunparser, std::string weaponname, int id)
 {
 	weaponDefs[id].name = weaponname;
 
@@ -185,19 +188,8 @@
 
 //	logOutput.Print("%s as %s",weaponname.c_str(),weaponDefs[id].type.c_str());
 
-	sunparser->GetDef(weaponDefs[id].firesound.name, "", weaponname + "\\soundstart");
-	sunparser->GetDef(weaponDefs[id].soundhit.name, "", weaponname + "\\soundhit");
-	sunparser->GetDef(weaponDefs[id].firesound.volume, "-1", weaponname + "\\soundstartvolume");
-	sunparser->GetDef(weaponDefs[id].soundhit.volume, "-1", weaponname + "\\soundhitvolume");
 
-	/*if(weaponDefs[id].firesound.name.find(".wav") == -1)
-		weaponDefs[id].firesound.name = weaponDefs[id].firesound.name + ".wav";
-	if(weaponDefs[id].soundhit.name.find(".wav") == -1)
-		weaponDefs[id].soundhit.name = weaponDefs[id].soundhit.name + ".wav";*/
 
-	//weaponDefs[id].firesoundVolume = 5.0f;
-	//weaponDefs[id].soundhitVolume = 5.0f;
-
 	weaponDefs[id].range = atof(sunparser->SGetValueDef("10", weaponname + "\\range").c_str());
 	float accuracy,sprayangle,movingAccuracy;
 	sunparser->GetDef(accuracy, "0", weaponname + "\\accuracy");
@@ -476,34 +468,57 @@
 	weaponDefs[id].dynDamageExp = atof(sunparser->SGetValueDef("0", weaponname + "\\dynDamageExp").c_str());
 	weaponDefs[id].dynDamageMin = atof(sunparser->SGetValueDef("0", weaponname + "\\dynDamageMin").c_str());
 	weaponDefs[id].dynDamageRange = atof(sunparser->SGetValueDef("0", weaponname + "\\dynDamageRange").c_str());
+
+
+	LoadSound(sunparser, weaponDefs[id].firesound, id, "firesound");
+	LoadSound(sunparser, weaponDefs[id].soundhit, id, "soundhit");
 }
 
-void CWeaponDefHandler::LoadSound(GuiSound &gsound)
+
+
+void CWeaponDefHandler::LoadSound(TdfParser* sunparser, GuiSound& gsound, int id, string soundCat)
 {
-	// gsound.volume = 5.0f;
-	if (gsound.name.compare("") == 0) {
-		gsound.id = 0;
-		return;
-	}
+	string name = "";
+	float volume = -1;
 
-	if (gsound.name.find(".wav") == -1) {
-		gsound.name = gsound.name + ".wav";
+	if (soundCat == "firesound") {
+		sunparser->GetDef(name, "", weaponDefs[id].name + "\\soundstart");
+		sunparser->GetDef(volume, "-1", weaponDefs[id].name + "\\soundstartvolume");
+	} else if (soundCat == "soundhit") {
+		sunparser->GetDef(name, "", weaponDefs[id].name + "\\soundhit");
+		sunparser->GetDef(volume, "-1", weaponDefs[id].name + "\\soundhitvolume");
 	}
 
-	const string soundPath = "sounds/" + gsound.name;
-	CFileHandler sfile(soundPath);
-	if (!sfile.FileExists()) {
-		gsound.id = 0;
-		return;
+	if (name != "") {
+		// only push data if we extracted a valid name
+		gsound.names.push_back(name);
+		gsound.ids.push_back(0);
+		gsound.volumes.push_back(volume);
+
+		if (name.find(".wav") == -1) {
+			// .wav extension missing, add it
+			gsound.setName(0, name + ".wav");
+		}
+
+		const string soundPath = "sounds/" + gsound.getName(0);
+		CFileHandler sfile(soundPath);
+
+		if (!sfile.FileExists()) {
+			// name refers to non-existent file, return
+			return;
+		}
+
+		PUSH_CODE_MODE;
+		ENTER_UNSYNCED;
+		int id = sound->GetWaveId(soundPath);
+		POP_CODE_MODE;
+
+		gsound.setID(0, id);
 	}
-	PUSH_CODE_MODE;
-	ENTER_UNSYNCED;
-	int id = sound->GetWaveId(soundPath);
-	POP_CODE_MODE;
-	gsound.id = id;
 }
 
 
+
 WeaponDef *CWeaponDefHandler::GetWeapon(const std::string weaponname2)
 {
 	std::string weaponname(StringToLower(weaponname2));
Index: Sim/Weapons/WeaponDefHandler.h
===================================================================
--- Sim/Weapons/WeaponDefHandler.h	(revision 3795)
+++ Sim/Weapons/WeaponDefHandler.h	(working copy)
@@ -38,12 +38,6 @@
 	std::string type;
 	std::string description;
 
-	/*std::string sfiresound;
-	std::string ssoundhit;
-	int firesoundId;
-	int soundhitId;
-	float firesoundVolume;
-	float soundhitVolume;*/
 	GuiSound firesound;
 	GuiSound soundhit;
 
@@ -192,8 +186,10 @@
 	bool dynDamageInverted;
 };
 
+
 class CExplosionGeneratorHandler;
 
+
 class CWeaponDefHandler
 {
 public:
@@ -204,9 +200,9 @@
 	CWeaponDefHandler();
 	~CWeaponDefHandler();
 
-	WeaponDef *GetWeapon(const std::string weaponname);
+	WeaponDef* GetWeapon(const std::string weaponname);
 
-	static void LoadSound(GuiSound &gsound);
+	void LoadSound(TdfParser*, GuiSound&, int, std::string);
 
 	DamageArray DynamicDamages(DamageArray damages, float3 startPos, float3 curPos, float range, float exp, float damageMin, bool inverted);
 
@@ -215,6 +211,7 @@
 	float3 hs2rgb(float h, float s);
 };
 
+
 extern CWeaponDefHandler* weaponDefHandler;
 
 
Index: Game/SelectedUnits.cpp
===================================================================
--- Game/SelectedUnits.cpp	(revision 3795)
+++ Game/SelectedUnits.cpp	(working copy)
@@ -324,10 +324,15 @@
 
 	SendCommand(c);
 
-	if(!selectedUnits.empty()){
+	if (!selectedUnits.empty()) {
 		set<CUnit*>::iterator ui = selectedUnits.begin();
-		if((*ui)->unitDef->sounds.ok.id)
-			sound->PlayUnitReply((*ui)->unitDef->sounds.ok.id, (*ui), (*ui)->unitDef->sounds.ok.volume, true);
+
+		int soundIdx = (*ui)->unitDef->sounds.ok.getRandomIdx();
+		if (soundIdx >= 0) {
+			sound->PlayUnitReply(
+				(*ui)->unitDef->sounds.ok.getID(soundIdx), (*ui),
+				(*ui)->unitDef->sounds.ok.getVolume(soundIdx), true);
+		}
 	}
 }
 
Index: Game/UI/MouseHandler.cpp
===================================================================
--- Game/UI/MouseHandler.cpp	(revision 3795)
+++ Game/UI/MouseHandler.cpp	(working copy)
@@ -405,10 +405,13 @@
 					}
 				}
 			}
-			if(addedunits==1)
-			{
-				if(unit->unitDef->sounds.select.id)
-					sound->PlayUnitReply(unit->unitDef->sounds.select.id, unit, unit->unitDef->sounds.select.volume);
+			if (addedunits == 1) {
+				int soundIdx = unit->unitDef->sounds.select.getRandomIdx();
+				if (soundIdx >= 0) {
+					sound->PlayUnitReply(
+						unit->unitDef->sounds.select.getID(soundIdx), unit,
+						unit->unitDef->sounds.select.getVolume(soundIdx));
+				}
 			}
 			else if(addedunits) //more than one unit selected
 				sound->PlaySample(soundMultiselID);
@@ -452,9 +455,12 @@
 				}
 				buttons[button].lastRelease=gu->gameTime;
 
-				if(unit->unitDef->sounds.select.id)
-					sound->PlayUnitReply(unit->unitDef->sounds.select.id, unit,
-					                     unit->unitDef->sounds.select.volume);
+				int soundIdx = unit->unitDef->sounds.select.getRandomIdx();
+				if (soundIdx >= 0) {
+					sound->PlayUnitReply(
+						unit->unitDef->sounds.select.getID(soundIdx), unit,
+						unit->unitDef->sounds.select.getVolume(soundIdx));
+				}
 			}
 		}
 	}
Index: Game/UI/MiniMap.cpp
===================================================================
--- Game/UI/MiniMap.cpp	(revision 3795)
+++ Game/UI/MiniMap.cpp	(working copy)
@@ -692,9 +692,11 @@
 			sound->PlaySample(mouse->soundMultiselID);
 		}
 		else if (addedunits == 1) {
-			if (unit->unitDef->sounds.select.id) {
-				sound->PlayUnitReply(unit->unitDef->sounds.select.id, unit,
-														 unit->unitDef->sounds.select.volume);
+			int soundIdx = unit->unitDef->sounds.select.getRandomIdx();
+			if (soundIdx >= 0) {
+				sound->PlayUnitReply(
+					unit->unitDef->sounds.select.getID(soundIdx), unit,
+					unit->unitDef->sounds.select.getVolume(soundIdx));
 			}
 		}
 	}
@@ -747,9 +749,11 @@
 			}
 			bp.lastRelease = gu->gameTime;
 
-			if (unit->unitDef->sounds.select.id) {
-				sound->PlayUnitReply(unit->unitDef->sounds.select.id, unit,
-														 unit->unitDef->sounds.select.volume);
+			int soundIdx = unit->unitDef->sounds.select.getRandomIdx();
+			if (soundIdx >= 0) {
+				sound->PlayUnitReply(
+					unit->unitDef->sounds.select.getID(soundIdx), unit,
+					unit->unitDef->sounds.select.getVolume(soundIdx));
 			}
 		}
 	}