AudioAreaAmbience.lua 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. ----------------------------------------------------------------------------------------------------
  2. --
  3. -- Copyright (c) Contributors to the Open 3D Engine Project.
  4. -- For complete copyright and license terms please see the LICENSE at the root of this distribution.
  5. --
  6. -- SPDX-License-Identifier: Apache-2.0 OR MIT
  7. --
  8. --
  9. --
  10. ----------------------------------------------------------------------------------------------------
  11. -- audio area ambience entity - to be attached to an area
  12. -- used for convenient implementation of area based audio ambiences
  13. AudioAreaAmbience = {
  14. type = "AudioAreaAmbience",
  15. Editor = {
  16. Model = "Editor/Objects/Sound.cgf",
  17. Icon = "AudioAreaAmbience.bmp",
  18. },
  19. Properties = {
  20. bEnabled = true,
  21. audioTriggerPlayTrigger = "",
  22. audioTriggerStopTrigger = "",
  23. audioRTPCRtpc = "",
  24. audioEnvironmentEnvironment = "",
  25. eiSoundObstructionType = AUDIO_OBSTRUCTION_TYPE_IGNORE,
  26. fRtpcDistance = 5.0,
  27. fEnvironmentDistance = 5.0,
  28. },
  29. fFadeValue = 0.0,
  30. nState = 0, -- 0 = far, 1 = near, 2 = inside
  31. fFadeOnUnregister = 1.0,
  32. hOnTriggerID = nil,
  33. hOffTriggerID = nil,
  34. hCurrentOnTriggerID = nil,
  35. hCurrentOffTriggerID = nil, -- only used in OnPropertyChange()
  36. hRtpcID = nil,
  37. hEnvironmentID = nil,
  38. tObstructionType = {},
  39. bIsHidden = false,
  40. bIsPlaying = false,
  41. bOriginalEnabled = true,
  42. }
  43. ----------------------------------------------------------------------------------------
  44. function AudioAreaAmbience:_LookupControlIDs()
  45. self.hOnTriggerID = AudioUtils.LookupTriggerID(self.Properties.audioTriggerPlayTrigger);
  46. self.hOffTriggerID = AudioUtils.LookupTriggerID(self.Properties.audioTriggerStopTrigger);
  47. self.hRtpcID = AudioUtils.LookupRtpcID(self.Properties.audioRTPCRtpc);
  48. self.hEnvironmentID = AudioUtils.LookupAudioEnvironmentID(self.Properties.audioEnvironmentEnvironment);
  49. end
  50. ----------------------------------------------------------------------------------------
  51. function AudioAreaAmbience:_LookupObstructionSwitchIDs()
  52. -- cache the obstruction switch and state IDs
  53. self.tObstructionType = AudioUtils.LookupObstructionSwitchAndStates();
  54. end
  55. ----------------------------------------------------------------------------------------
  56. function AudioAreaAmbience:_SetObstruction()
  57. local nStateIdx = self.Properties.eiSoundObstructionType + 1;
  58. self:SetAudioObstructionCalcType(self.Properties.eiSoundObstructionType, self:GetDefaultAuxAudioProxyID());
  59. if ((self.tObstructionType.hSwitchID ~= nil) and (self.tObstructionType.tStateIDs[nStateIdx] ~= nil)) then
  60. self:SetAudioSwitchState(self.tObstructionType.hSwitchID, self.tObstructionType.tStateIDs[nStateIdx], self:GetDefaultAuxAudioProxyID());
  61. end
  62. end
  63. ----------------------------------------------------------------------------------------
  64. function AudioAreaAmbience:_DisableObstruction()
  65. -- Ignore is at index 1 (because Lua uses 1-based indexing)
  66. local nStateIdx = 1;
  67. self:SetAudioObstructionCalcType(AUDIO_OBSTRUCTION_TYPE_IGNORE, self:GetDefaultAuxAudioProxyID());
  68. if ((self.tObstructionType.hSwitchID ~= nil) and (self.tObstructionType.tStateIDs[nStateIdx] ~= nil)) then
  69. self:SetAudioSwitchState(self.tObstructionType.hSwitchID, self.tObstructionType.tStateIDs[nStateIdx], self:GetDefaultAuxAudioProxyID());
  70. end
  71. end
  72. ----------------------------------------------------------------------------------------
  73. function AudioAreaAmbience:_UpdateParameters()
  74. -- Set the distances as the very first thing!
  75. self:SetFadeDistance(self.Properties.fRtpcDistance);
  76. self:SetEnvironmentFadeDistance(self.Properties.fEnvironmentDistance);
  77. if (self.Properties.bEnabled) then
  78. if (self.hEnvironmentID ~= nil) then
  79. self:SetAudioEnvironmentID(self.hEnvironmentID);
  80. end
  81. else
  82. self:SetAudioEnvironmentID(INVALID_AUDIO_ENVIRONMENT_ID);
  83. end
  84. end
  85. ----------------------------------------------------------------------------------------
  86. function AudioAreaAmbience:_UpdateRtpc()
  87. if (self.hRtpcID ~= nil) then
  88. self:SetAudioRtpcValue(self.hRtpcID, self.fFadeValue, self:GetDefaultAuxAudioProxyID());
  89. end
  90. end
  91. ----------------------------------------------------------------------------------------
  92. function AudioAreaAmbience:OnSpawn()
  93. self:SetFlags(ENTITY_FLAG_CLIENT_ONLY, 0);
  94. end
  95. ----------------------------------------------------------------------------------------
  96. function AudioAreaAmbience:OnLoad(load)
  97. self.Properties = load.Properties;
  98. self.fFadeValue = load.fFadeValue;
  99. self.fFadeOnUnregister = load.fFadeOnUnregister;
  100. self.nState = 0; -- We start out being far, in a subsequent update we will determine our actual state!
  101. self:_SetObstruction();
  102. end
  103. ----------------------------------------------------------------------------------------
  104. function AudioAreaAmbience:OnPostLoad()
  105. self:_UpdateParameters();
  106. end
  107. ----------------------------------------------------------------------------------------
  108. function AudioAreaAmbience:OnSave(save)
  109. save.Properties = self.Properties;
  110. save.fFadeValue = self.fFadeValue;
  111. save.fFadeOnUnregister = self.fFadeOnUnregister;
  112. end
  113. ----------------------------------------------------------------------------------------
  114. function AudioAreaAmbience:OnPropertyChange()
  115. if (self.Properties.eiSoundObstructionType < AUDIO_OBSTRUCTION_TYPE_IGNORE) then
  116. self.Properties.eiSoundObstructionType = AUDIO_OBSTRUCTION_TYPE_IGNORE;
  117. elseif (self.Properties.eiSoundObstructionType > AUDIO_OBSTRUCTION_TYPE_MULTI) then
  118. self.Properties.eiSoundObstructionType = AUDIO_OBSTRUCTION_TYPE_MULTI;
  119. end
  120. self:_LookupControlIDs();
  121. self:_UpdateParameters();
  122. self:ResetAudioRtpcValues(self:GetDefaultAuxAudioProxyID());
  123. self:SetCurrentAudioEnvironments();
  124. self:SetAudioProxyOffset(g_Vectors.v000, self:GetDefaultAuxAudioProxyID());
  125. if (self.nState == 1) then -- near
  126. self:_SetObstruction();
  127. elseif (self.nState == 2) then -- inside
  128. self:_DisableObstruction();
  129. end
  130. if ((self.bIsPlaying) and (self.hCurrentOnTriggerID ~= self.hOnTriggerID)) then
  131. -- Stop a possibly playing instance if the on-trigger changed!
  132. self:StopAudioTrigger(self.hCurrentOnTriggerID, self:GetDefaultAuxAudioProxyID());
  133. self.hCurrentOnTriggerID = self.hOnTriggerID;
  134. self.bIsPlaying = false;
  135. self.bHasMoved = false;
  136. end
  137. if (not self.bIsPlaying) then
  138. -- Try to play, if disabled, hidden or invalid on-trigger Play() will fail!
  139. self:Play();
  140. end
  141. if (not self.Properties.bEnabled and ((self.bOriginalEnabled) or (self.hCurrentOffTriggerID ~= self.hOffTriggerID))) then
  142. self.hCurrentOffTriggerID = self.hOffTriggerID;
  143. self:Stop(); -- stop if disabled, either stops running StartTrigger or executes StopTrigger!
  144. end
  145. self.bOriginalEnabled = self.Properties.bEnabled;
  146. end
  147. ----------------------------------------------------------------------------------------
  148. function AudioAreaAmbience:OnReset(bToGame)
  149. if (bToGame) then
  150. -- store the entity's "bEnabled" property's value so we can adjust back to it if changed over the course of the game
  151. self.bOriginalEnabled = self.Properties.bEnabled;
  152. -- re-execute this AAA once upon entering game mode
  153. self:Stop();
  154. self:Play();
  155. else
  156. self.Properties.bEnabled = self.bOriginalEnabled;
  157. end
  158. end
  159. ----------------------------------------------------------------------------------------
  160. function AudioAreaAmbience:Play()
  161. if ((self.Properties.bEnabled) and (not self.bIsHidden) and ((self.nState == 1) or (self.nState == 2))) then
  162. if (self.hOnTriggerID ~= nil) then
  163. self:SetCurrentAudioEnvironments();
  164. self:ExecuteAudioTrigger(self.hOnTriggerID, self:GetDefaultAuxAudioProxyID());
  165. self.bIsPlaying = true;
  166. self.hCurrentOnTriggerID = self.hOnTriggerID;
  167. end
  168. end
  169. end
  170. ----------------------------------------------------------------------------------------
  171. function AudioAreaAmbience:Stop()
  172. if ((self.Properties.bEnabled) and (not self.bIsHidden) and ((self.nState == 1) or (self.nState == 2))) then
  173. -- cannot check against "self.bIsPlaying" otherwise we won't execute the StopTrigger if there's no StartTrigger set!
  174. if (self.hOffTriggerID ~= nil) then
  175. self:ExecuteAudioTrigger(self.hOffTriggerID, self:GetDefaultAuxAudioProxyID());
  176. elseif (self.hOnTriggerID ~= nil) then
  177. self:StopAudioTrigger(self.hOnTriggerID, self:GetDefaultAuxAudioProxyID());
  178. end
  179. end
  180. self.bIsPlaying = false;
  181. end
  182. ----------------------------------------------------------------------------------------
  183. function AudioAreaAmbience:StopAll()
  184. if (self.hOnTriggerID ~= nil) then
  185. self:StopAudioTrigger(self.hOnTriggerID, self:GetDefaultAuxAudioProxyID());
  186. end
  187. if (self.hOffTriggerID ~= nil) then
  188. self:StopAudioTrigger(self.hOffTriggerID, self:GetDefaultAuxAudioProxyID());
  189. end
  190. self.bIsPlaying = false;
  191. end
  192. ----------------------------------------------------------------------------------------
  193. function AudioAreaAmbience:CliSrv_OnInit()
  194. self.nState = 0;
  195. self.fFadeValue = 0.0;
  196. self.fFadeOnUnregister = 1.0;
  197. self:SetFlags(ENTITY_FLAG_VOLUME_SOUND, 0);
  198. self:_UpdateParameters();
  199. self.bIsPlaying = false;
  200. self:NetPresent(0);
  201. end
  202. ----------------------------------------------------------------------------------------
  203. function AudioAreaAmbience:UpdateFadeValue(player, fFade, fDistSq)
  204. if (not(self.Properties.bEnabled) or (fFade == 0.0 and fDistSq == 0.0)) then
  205. self.fFadeValue = 0.0;
  206. self:_UpdateRtpc();
  207. do return end;
  208. end
  209. if (self.Properties.fRtpcDistance > 0.0) then
  210. if (self.nState == 2) then
  211. if (self.fFadeValue ~= fFade) then
  212. self.fFadeValue = math.abs(fFade);
  213. self:_UpdateRtpc();
  214. end
  215. else
  216. local fLocalFade = 1.0 - (math.sqrt(fDistSq) / self.Properties.fRtpcDistance);
  217. self.fFadeValue = math.max(0, fLocalFade);
  218. self:_UpdateRtpc();
  219. end
  220. end
  221. end
  222. ----------------------------------------------------------------------------------------
  223. AudioAreaAmbience.Server = {
  224. OnInit = function(self)
  225. self:CliSrv_OnInit();
  226. end,
  227. OnShutDown = function(self)
  228. end,
  229. }
  230. ----------------------------------------------------------------------------------------
  231. AudioAreaAmbience.Client = {
  232. OnInit = function(self)
  233. self:RegisterForAreaEvents(1);
  234. self:_LookupControlIDs();
  235. self:_LookupObstructionSwitchIDs();
  236. self:_SetObstruction();
  237. self:CliSrv_OnInit();
  238. end,
  239. OnShutDown = function(self)
  240. self:StopAll();
  241. self.nState = 0;
  242. self:RegisterForAreaEvents(0);
  243. end,
  244. OnHidden = function(self)
  245. self:StopAll();
  246. self.bIsHidden = true;
  247. end,
  248. OnUnHidden = function(self)
  249. self.bIsHidden = false;
  250. self:Play();
  251. end,
  252. OnAudioListenerEnterNearArea = function(self, player, nAreaID, fFade)
  253. if (self.nState == 0) then
  254. self.nState = 1;
  255. self:Play();
  256. self.fFadeValue = 0.0;
  257. self:_UpdateRtpc();
  258. end
  259. end,
  260. OnAudioListenerMoveNearArea = function(self, player, areaId, fFade, fDistsq)
  261. self.nState = 1;
  262. self:UpdateFadeValue(player, fFade, fDistsq);
  263. end,
  264. OnAudioListenerEnterArea = function(self, player, areaId, fFade)
  265. if (self.nState == 0) then
  266. -- possible if the listener is teleported or gets spawned inside the area
  267. -- technically, the listener enters the Near Area and the Inside Area at the same time
  268. self.nState = 2;
  269. self:Play();
  270. else
  271. self.nState = 2;
  272. end
  273. self.fFadeValue = 1.0;
  274. self:_UpdateRtpc();
  275. self:_DisableObstruction();
  276. end,
  277. OnAudioListenerProceedFadeArea = function(self, player, areaId, fExternalFade)
  278. -- fExternalFade holds the fade value which was calculated by an inner, higher priority area
  279. -- in the AreaManager to fade out the outer sound dependent on the largest fade distance of all attached entities
  280. if (fExternalFade > 0.0) then
  281. self.nState = 2;
  282. self:UpdateFadeValue(player, fExternalFade, 0.0);
  283. else
  284. self:UpdateFadeValue(player, 0.0, 0.0);
  285. end
  286. end,
  287. OnAudioListenerLeaveArea = function(self, player, nAreaID, fFade)
  288. self.nState = 1;
  289. self:_SetObstruction();
  290. end,
  291. OnAudioListenerLeaveNearArea = function(self, player, nAreaID, fFade)
  292. self:Stop();
  293. self.nState = 0;
  294. self.fFadeValue = 0.0;
  295. self:_UpdateRtpc();
  296. end,
  297. OnUnBindThis = function(self)
  298. self.nState = 0;
  299. end,
  300. OnSoundDone = function(self, hTriggerID)
  301. if (self.hOnTriggerID == hTriggerID) then
  302. self:ActivateOutput("Done", true);
  303. end
  304. end,
  305. }
  306. ----------------------------------------------------------------------------------------
  307. -- Event Handlers
  308. ----------------------------------------------------------------------------------------
  309. function AudioAreaAmbience:Event_Enable(sender)
  310. self.Properties.bEnabled = true;
  311. self:OnPropertyChange();
  312. end
  313. function AudioAreaAmbience:Event_Disable(sender)
  314. self.Properties.bEnabled = false;
  315. self:OnPropertyChange();
  316. end
  317. AudioAreaAmbience.FlowEvents =
  318. {
  319. Inputs =
  320. {
  321. Enable = { AudioAreaAmbience.Event_Enable, "bool" },
  322. Disable = { AudioAreaAmbience.Event_Disable, "bool" },
  323. },
  324. Outputs =
  325. {
  326. Done = "bool",
  327. },
  328. }