AudioAreaEntity.lua 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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 entity - to be attached to an area
  12. -- reports a normalized (0-1) fade value depending on the listener's distance to the area
  13. AudioAreaEntity = {
  14. type = "AudioAreaEntity",
  15. Editor = {
  16. Model = "Editor/Objects/Sound.cgf",
  17. Icon = "AudioAreaEntity.bmp",
  18. },
  19. Properties = {
  20. bEnabled = true,
  21. audioEnvironmentEnvironment = "",
  22. eiSoundObstructionType = AUDIO_OBSTRUCTION_TYPE_IGNORE,
  23. fFadeDistance = 5.0,
  24. fEnvironmentDistance = 5.0,
  25. },
  26. fFadeValue = 0.0,
  27. nState = 0, -- 0 = far, 1 = near, 2 = inside
  28. fFadeOnUnregister = 1.0,
  29. hEnvironmentID = nil,
  30. tObstructionType = {},
  31. }
  32. ----------------------------------------------------------------------------------------
  33. function AudioAreaEntity:_ActivateOutput(sPortName, value)
  34. if (self.Properties.bEnabled) then
  35. self:ActivateOutput(sPortName, value);
  36. end
  37. end
  38. ----------------------------------------------------------------------------------------
  39. function AudioAreaEntity:_UpdateParameters()
  40. -- Set the distances as the very first thing!
  41. self:SetFadeDistance(self.Properties.fFadeDistance);
  42. self:SetEnvironmentFadeDistance(self.Properties.fEnvironmentDistance);
  43. if (self.Properties.bEnabled) then
  44. self.hEnvironmentID = AudioUtils.LookupAudioEnvironmentID(self.Properties.audioEnvironmentEnvironment);
  45. if (self.hEnvironmentID ~= nil) then
  46. self:SetAudioEnvironmentID(self.hEnvironmentID);
  47. end
  48. else
  49. self:SetAudioEnvironmentID(INVALID_AUDIO_ENVIRONMENT_ID);
  50. end
  51. end
  52. ----------------------------------------------------------------------------------------
  53. function AudioAreaEntity:_LookupObstructionSwitchIDs()
  54. -- cache the obstruction switch and state IDs
  55. self.tObstructionType = AudioUtils.LookupObstructionSwitchAndStates();
  56. end
  57. ----------------------------------------------------------------------------------------
  58. function AudioAreaEntity:_SetObstruction()
  59. local nStateIdx = self.Properties.eiSoundObstructionType + 1;
  60. self:SetAudioObstructionCalcType(self.Properties.eiSoundObstructionType, self:GetDefaultAuxAudioProxyID());
  61. if ((self.tObstructionType.hSwitchID ~= nil) and (self.tObstructionType.tStateIDs[nStateIdx] ~= nil)) then
  62. self:SetAudioSwitchState(self.tObstructionType.hSwitchID, self.tObstructionType.tStateIDs[nStateIdx], self:GetDefaultAuxAudioProxyID());
  63. end
  64. end
  65. ----------------------------------------------------------------------------------------
  66. function AudioAreaEntity:OnSpawn()
  67. self:SetFlags(ENTITY_FLAG_CLIENT_ONLY, 0);
  68. end
  69. ----------------------------------------------------------------------------------------
  70. function AudioAreaEntity:OnLoad(load)
  71. self.Properties = load.Properties;
  72. self.fFadeOnUnregister = load.fFadeOnUnregister;
  73. self:_SetObstruction();
  74. self.nState = 0;
  75. self.fFadeValue = 0.0;
  76. end
  77. ----------------------------------------------------------------------------------------
  78. function AudioAreaEntity:OnPostLoad()
  79. self:_UpdateParameters();
  80. end
  81. ----------------------------------------------------------------------------------------
  82. function AudioAreaEntity:OnSave(save)
  83. save.Properties = self.Properties;
  84. save.fFadeOnUnregister = self.fFadeOnUnregister;
  85. end
  86. ----------------------------------------------------------------------------------------
  87. function AudioAreaEntity:OnPropertyChange()
  88. self:_UpdateParameters();
  89. if (self.Properties.eiSoundObstructionType < AUDIO_OBSTRUCTION_TYPE_IGNORE) then
  90. self.Properties.eiSoundObstructionType = AUDIO_OBSTRUCTION_TYPE_IGNORE;
  91. elseif (self.Properties.eiSoundObstructionType > AUDIO_OBSTRUCTION_TYPE_MULTI) then
  92. self.Properties.eiSoundObstructionType = AUDIO_OBSTRUCTION_TYPE_MULTI;
  93. end
  94. self:ResetAudioRtpcValues(self:GetDefaultAuxAudioProxyID());
  95. self:_SetObstruction();
  96. end
  97. ----------------------------------------------------------------------------------------
  98. function AudioAreaEntity:CliSrv_OnInit()
  99. self.nState = 0;
  100. self.fFadeValue = 0.0;
  101. self.fFadeOnUnregister = 1.0;
  102. self:SetFlags(ENTITY_FLAG_VOLUME_SOUND, 0);
  103. self:_UpdateParameters();
  104. end
  105. ----------------------------------------------------------------------------------------
  106. function AudioAreaEntity:UpdateFadeValue(player, fFade, fDistSq)
  107. if (not(self.Properties.bEnabled) or (fFade == 0.0 and fDistSq == 0.0)) then
  108. if (self.fFadeValue ~= 0.0) then
  109. self:_ActivateOutput("FadeValue", 0.0);
  110. end
  111. self.fFadeValue = 0.0;
  112. do return end;
  113. end
  114. if (self.Properties.fFadeDistance > 0.0) then
  115. if (self.nState == 2) then
  116. if (self.fFadeValue ~= fFade) then
  117. self.fFadeValue = math.abs(fFade);
  118. self:_ActivateOutput("FadeValue", self.fFadeValue);
  119. end
  120. else
  121. local fLocalFade = 1.0 - (math.sqrt(fDistSq) / self.Properties.fFadeDistance);
  122. self.fFadeValue = math.max(0, fLocalFade);
  123. self:_ActivateOutput("FadeValue", self.fFadeValue);
  124. end
  125. end
  126. end
  127. ----------------------------------------------------------------------------------------
  128. AudioAreaEntity.Server = {
  129. OnInit = function(self)
  130. self:CliSrv_OnInit();
  131. end,
  132. OnShutDown = function(self)
  133. end,
  134. }
  135. ----------------------------------------------------------------------------------------
  136. AudioAreaEntity.Client = {
  137. OnInit = function(self)
  138. self:RegisterForAreaEvents(1);
  139. self:_LookupObstructionSwitchIDs();
  140. self:_SetObstruction();
  141. self:CliSrv_OnInit();
  142. end,
  143. OnShutDown = function(self)
  144. self.nState = 0;
  145. self:RegisterForAreaEvents(0);
  146. end,
  147. OnAudioListenerEnterNearArea = function(self, player, nAreaID, fFade)
  148. if (self.nState == 0) then
  149. self:_SetObstruction();
  150. self:_ActivateOutput("OnFarToNear", true);
  151. elseif (self.nState == 2) then
  152. self:_ActivateOutput("OnInsideToNear", true);
  153. end
  154. self.nState = 1;
  155. self.fFadeValue = 0.0;
  156. self:_ActivateOutput("FadeValue", self.fFadeValue);
  157. end,
  158. OnAudioListenerMoveNearArea = function(self, player, areaId, fFade, fDistsq)
  159. self.nState = 1;
  160. self:UpdateFadeValue(player, fFade, fDistsq);
  161. end,
  162. OnAudioListenerEnterArea = function(self, player, areaId, fFade)
  163. if (self.nState == 0) then
  164. -- possible if the listener is teleported or gets spawned inside the area
  165. -- technically, the listener enters the Near Area and the Inside Area at the same time
  166. -- however, in this case the AreaManager is responsible to first call OnEnterNear and then OnEnter so this is technically circumventing a possible bug :)
  167. self:_SetObstruction();
  168. self:_ActivateOutput("OnFarToNear", true);
  169. end
  170. self.nState = 2;
  171. self.fFadeValue = 1.0;
  172. self:_ActivateOutput("OnNearToInside", true);
  173. self:_ActivateOutput("FadeValue", self.fFadeValue);
  174. end,
  175. OnAudioListenerProceedFadeArea = function(self, player, areaId, fExternalFade)
  176. -- fExternalFade holds the fade value which was calculated by an inner, higher priority area
  177. -- in the AreaManager to fade out the outer sound dependent on the largest fade distance of all attached entities
  178. if (fExternalFade > 0.0) then
  179. self.nState = 2;
  180. self:UpdateFadeValue(player, fExternalFade, 0.0);
  181. else
  182. self:UpdateFadeValue(player, 0.0, 0.0);
  183. end
  184. end,
  185. OnAudioListenerLeaveArea = function(self, player, nAreaID, fFade)
  186. self.nState = 1;
  187. self:_ActivateOutput("OnInsideToNear", true);
  188. end,
  189. OnAudioListenerLeaveNearArea = function(self, player, nAreaID, fFade)
  190. self.nState = 0;
  191. self.fFadeValue = 0.0;
  192. self:_ActivateOutput("OnNearToFar", true);
  193. self:_ActivateOutput("FadeValue", self.fFadeValue);
  194. end,
  195. OnUnBindThis = function(self)
  196. self.nState = 0;
  197. end,
  198. }
  199. ----------------------------------------------------------------------------------------
  200. -- Event Handlers
  201. ----------------------------------------------------------------------------------------
  202. function AudioAreaEntity:Event_Enable(sender)
  203. self.Properties.bEnabled = true;
  204. self:OnPropertyChange();
  205. end
  206. function AudioAreaEntity:Event_Disable(sender)
  207. self.Properties.bEnabled = false;
  208. self:OnPropertyChange();
  209. end
  210. AudioAreaEntity.FlowEvents =
  211. {
  212. Inputs =
  213. {
  214. Enable = { AudioAreaEntity.Event_Enable, "bool" },
  215. Disable = { AudioAreaEntity.Event_Disable, "bool" },
  216. },
  217. Outputs =
  218. {
  219. FadeValue = "float",
  220. OnFarToNear = "bool",
  221. OnNearToInside = "bool",
  222. OnInsideToNear = "bool",
  223. OnNearToFar = "bool",
  224. },
  225. }