Loading

Paste #pkjxupvgx

  1. commit b19ed8d29075ab55ec00b24e8e622f69b1b22939
  2. Author: JASheppard <J_Sheppard@live.co.uk>
  3. Date:   Thu Mar 13 20:29:44 2014 +0000
  4.  
  5.     New function: playSoundsAtEntityInRandomSequence()
  6.    
  7.     This function is required to play the lava hole's sounds because its sounds are meant to be played in a random sequence and are therefore not associated with animation frames, so the animation code can't play these sounds.
  8.    
  9.     Change list
  10.     -----------
  11.     1. app.lua:
  12.     a) SAVEGAME_VERSION = 87: Required in this commit because a new afterLoad() sound playing function has been added.
  13.     b) +afterLoad() call to world:playLoadedEntitySounds()
  14.    
  15.     2. audio.lua:
  16.     a) +self.entities_waiting_for_sound_to_be_enabled{}: Required to support the resumption of sounds being played by this commit's new audio playing function.
  17.     b) +playSoundsAtEntityInRandomSequence()
  18.     c) +playSoundAtEntityInRandomSequenceRecursionHandler()
  19.     d) +getRandomSilenceLengths(): Required to provide different durations for the sound seperating pauses in this commit's new audio playing function.
  20.     e) Modified playSound(): Moved its sound caching statements into a new function to prevent them from having to be duplicated in this commit's new audio playing function.
  21.     f) +canSoundsBePlayed(): Required to support the resumption of sounds being played by this commit's new audio playing function.
  22.     g) +onEndPause(paused): Required to support the resumption of sounds being played by this commit's new audio playing function.
  23.     h) +tellInterestedEntitiesTheyCanNowPlaySounds(): Required to support the resumption of sounds being played by this commit's new audio playing function.
  24.     i) +calls to tellInterestedEntitiesTheyCanNowPlaySounds()
  25.     j) +entityNoLongerWaitingForSoundsToBeTurnedOn(entity): Required to prevent the resumption of sounds being played for an entity which no longer exists or no longer wants its sounds to be resumed.
  26.    
  27.     3. entity.lua:
  28.     a) +Variables for this commit's new audio playing function.
  29.     b) +playSoundsAtEntityInRandomSequence()
  30.     c) +playAfterLoadSound(): Required to support the resumption of sounds being played by this commit's new audio playing function.
  31.     d) +setWaitingForSoundEffectsToBeTurnedOn(state): Required to call audio:entityNoLongerWaitingForSoundsToBeTurnedOn()
  32.    
  33.     4. game_ui.lua:
  34.     Modified togglePlaySounds() so that the keyboard shortcut will have the same response as clicking the menu option allowing it to disable and enable sounds being played by this commit's new audio playing function.
  35.    
  36.     5. world.lua:
  37.     a) +Call to audio:onEndPause(): Required to support the resumption of sounds being played by this commit's new audio playing function.
  38.     b) +isPaused(): Required to support the resumption of sounds being played by this commit's new audio playing function.
  39.     c) Modified afterLoad() to make it support the resumption of sounds being played by this commit's new audio playing function.
  40.     d) +playLoadedEntitySounds(): Required to support the resumption of sounds being played by this commit's new audio playing function.
  41.  
  42. diff --git a/CorsixTH/Lua/app.lua b/CorsixTH/Lua/app.lua
  43. index e806dd7..e94fcb5 100644
  44. --- a/CorsixTH/Lua/app.lua
  45. +++ b/CorsixTH/Lua/app.lua
  46. @@ -29,7 +29,7 @@ local assert, io, type, dofile, loadfile, pcall, tonumber, print, setmetatable
  47.  -- Increment each time a savegame break would occur
  48.  -- and add compatibility code in afterLoad functions
  49.  
  50. -local SAVEGAME_VERSION = 86
  51. +local SAVEGAME_VERSION = 87
  52.  
  53.  class "App"
  54.  
  55. @@ -1248,6 +1248,7 @@ function App:afterLoad()
  56.    if new == old then
  57.      self.world:gameLog("Savegame version is " .. new .. " (" .. self:getVersion()
  58.        .. "), originally it was " .. first .. " (" .. self:getVersion(first) .. ")")
  59. +    self.world:playLoadedEntitySounds()
  60.      return
  61.    elseif new > old then
  62.      self.world:gameLog("Savegame version changed from " .. old .. " (" .. self:getVersion(old) ..
  63. diff --git a/CorsixTH/Lua/audio.lua b/CorsixTH/Lua/audio.lua
  64. index b28c4b1..4aa6dd8 100644
  65. --- a/CorsixTH/Lua/audio.lua
  66. +++ b/CorsixTH/Lua/audio.lua
  67. @@ -1,4 +1,4 @@
  68. -   --[[ Copyright (c) 2009 Peter "Corsix" Cawley
  69. +--[[ Copyright (c) 2009 Peter "Corsix" Cawley
  70.  
  71.  Permission is hereby granted, free of charge, to any person obtaining a copy of
  72.  this software and associated documentation files (the "Software"), to deal in
  73. @@ -36,11 +36,13 @@ function Audio:Audio(app)
  74.    self.not_loaded = not app.config.audio
  75.    self.unused_played_callback_id = 0
  76.    self.played_sound_callbacks = {}
  77. +  self.entities_waiting_for_sound_to_be_enabled = {}
  78.  end
  79.  
  80.  function Audio:clearCallbacks()
  81.    self.unused_played_callback_id = 0
  82.    self.played_sound_callbacks = {}
  83. +  self.entities_waiting_for_sound_to_be_enabled = {}
  84.  end
  85.  
  86.  local function GetFileData(path)
  87. @@ -264,18 +266,7 @@ function Audio:playSound(name, where, is_announcement, played_callback, played_c
  88.    if sound_fx then
  89.      if name:find("*") then
  90.        -- Resolve wildcard to one particular sound
  91. -      local list = wilcard_cache[name]
  92. -      if not list then
  93. -        list = {}
  94. -        wilcard_cache[name] = list
  95. -        local pattern = ("^" .. name:gsub("%*",".*") .. "$"):upper()
  96. -        for i = 1, #self.sound_archive - 1 do
  97. -          local filename = self.sound_archive:getFilename(i):upper()
  98. -          if filename:find(pattern) then
  99. -            list[#list + 1] = filename
  100. -          end
  101. -        end
  102. -      end
  103. +      local list = self:cacheSoundFilenamesAssociatedWithName(name)
  104.        name = list[1] and list[math.random(1, #list)] or name
  105.      end
  106.      local _, warning
  107. @@ -303,6 +294,138 @@ function Audio:playSound(name, where, is_announcement, played_callback, played_c
  108.    end
  109.  end
  110.  
  111. +function Audio:cacheSoundFilenamesAssociatedWithName(name)
  112. +  local list = wilcard_cache[name]
  113. +  if not list then
  114. +    local filename
  115. +    list = {}
  116. +    wilcard_cache[name] = list
  117. +    local pattern = ("^" .. name:gsub("%*",".*") .. "$"):upper()
  118. +    for i = 1, #self.sound_archive - 1 do
  119. +      filename = self.sound_archive:getFilename(i):upper()
  120. +      if filename:find(pattern) then
  121. +        list[#list + 1] = filename
  122. +      end
  123. +    end
  124. +  end
  125. +  return list
  126. +end
  127. +
  128. +--[[
  129. +Plays related sounds at an entity in a random sequence, with random length silences between the sounds.
  130. +
  131. +This function's integer array parameters for the min and max silence lengths should provide lengths
  132. +for this game's different speeds, indexed as follows:
  133. +[1] Slowest [2] Slow [3] Normal [4] Fast [5] Maximum Speed
  134. +
  135. +!param names (string) A name pattern for the sequence of related sounds to be played for example: LAVA00*.wav
  136. +!param entity : Where the sounds will be played at, the player won't hear the sounds being played at the entity
  137. +when it isn't in their view.
  138. +!param min_silence_lengths (integer array) The desired minimum silence lengths for this game's different speeds.
  139. +!param max_silence_lengths (integer array) The desired maximum silence lengths for this game's different speeds.
  140. +!param num_silences (integer) How many different silence lengths should be used, this can be a nil parameter.
  141. +--]]
  142. +function Audio:playSoundsAtEntityInRandomSequence(names, entity, min_silence_lengths, max_silence_lengths, num_silences)
  143. +  if self.sound_fx then
  144. +    self:cacheSoundFilenamesAssociatedWithName(names)
  145. +    self:playSoundsAtEntityInRandomSequenceRecursionHandler(wilcard_cache[names],
  146. +                                                            entity,
  147. +                                                            self:getRandomSilenceLengths(min_silence_lengths,
  148. +                                                                                         max_silence_lengths,
  149. +                                                                                         num_silences),
  150. +                                                            1)
  151. +  end
  152. +end
  153. +
  154. +--[[
  155. +Called by the above function.
  156. +
  157. +This function's integer array parameters for the min and max silence lengths should provide lengths
  158. +for this game's different speeds, indexed as follows:
  159. +[1] Slowest [2] Slow [3] Normal [4] Fast [5] Maximum Speed
  160. +
  161. +!param sounds (string) A name pattern for the sequence of related sounds to be played for example: LAVA00*.wav
  162. +!param entity : Where the sounds will be played at, the player won't hear the sounds being played at the entity
  163. +when it isn't in their view.
  164. +!param silences (integer array) the different pause durations to be used between the played sounds.
  165. +!param silences_pointer (integer) the index for the pause duration which should be used after this call's sound has been played.
  166. +--]]
  167. +function Audio:playSoundsAtEntityInRandomSequenceRecursionHandler(sounds, entity, silences, silences_pointer)
  168. +  if entity.playing_sounds_in_random_sequence then
  169. +    local sound_played_callback = function()
  170. +                                    self:playSoundsAtEntityInRandomSequenceRecursionHandler(sounds,
  171. +                                                                                            entity,
  172. +                                                                                            silences,
  173. +                                                                                            silences_pointer)
  174. +                                  end
  175. +
  176. +    if self:canSoundsBePlayed() then
  177. +      local _, warning
  178. +      local x, y = Map:WorldToScreen(entity.tile_x, entity.tile_y)
  179. +      local dx, dy = entity.th:getPosition()
  180. +      x = x + dx - self.app.ui.screen_offset_x
  181. +      y = y + dy - self.app.ui.screen_offset_y
  182. +
  183. +      self.played_sound_callbacks[tostring(self.unused_played_callback_id)] = sound_played_callback
  184. +      _, warning = self.sound_fx:play(sounds[math.random(1,#sounds)],
  185. +                                      self.app.config.sound_volume,
  186. +                                      x,
  187. +                                      y,
  188. +                                      self.unused_played_callback_id,
  189. +                                      silences_pointer)
  190. +
  191. +      self.unused_played_callback_id = self.unused_played_callback_id + 1
  192. +      if #silences > 1 then
  193. +        silences_pointer = (silences_pointer % #silences) + 1
  194. +      end
  195. +    --If the sound can't be played now:
  196. +    else
  197. +      self.entities_waiting_for_sound_to_be_enabled[entity] = sound_played_callback
  198. +       entity:setWaitingForSoundEffectsToBeTurnedOn(true)
  199. +     end
  200. +  else
  201. +    if self.entities_waiting_for_sound_to_be_enabled[entity] then
  202. +      self.entities_waiting_for_sound_to_be_enabled[entity] = nil
  203. +    end
  204. +  end
  205. +end
  206. +
  207. +function Audio:canSoundsBePlayed()
  208. +  return TheApp.config.play_sounds and not TheApp.world:isPaused()
  209. +end
  210. +
  211. +--[[
  212. +This function's integer array parameters for the min and max silence lengths should provide lengths
  213. +for this game's different speeds, indexed as follows:
  214. +[1] Slowest [2] Slow [3] Normal [4] Fast [5] Maximum
  215. +
  216. +!param min_silence_lengths (integer array) The desired minimum silence lengths for this game's different speeds.
  217. +!param max_silence_lengths (integer array) The desired maximum silence lengths for this game's different speeds.
  218. +!param num_silences (integer) How many silence lengths should be in the returned table of generated lengths.
  219. +!return (table) A table of randomly ordered integers for the generated silence lengths.
  220. +--]]
  221. +function Audio:getRandomSilenceLengths(min_silence_lengths, max_silence_lengths, num_silences)
  222. +  local min_silence = min_silence_lengths[TheApp.world.tick_rate]
  223. +  local max_silence = max_silence_lengths[TheApp.world.tick_rate]
  224. +
  225. +  local silences = {}
  226. +  if min_silence == max_silence then
  227. +    silences[1] = min_silence
  228. +  else
  229. +    for i = 1, num_silences do
  230. +      silences[i] = math.random(min_silence,max_silence)
  231. +    end
  232. +  end
  233. +
  234. +  return silences
  235. +end
  236. +
  237. +function Audio:onEndPause()
  238. +  if TheApp.config.play_sounds then
  239. +    self:tellInterestedEntitiesTheyCanNowPlaySounds()
  240. +  end
  241. +end
  242. +
  243.  function Audio:onSoundPlayed(played_callbacks_id)
  244.    if TheApp.world ~= nil then
  245.      if self.played_sound_callbacks[tostring(played_callbacks_id)] then
  246. @@ -525,6 +648,23 @@ function Audio:playSoundEffects(play_effects)
  247.      -- As above.
  248.      self.sound_fx:setSoundEffectsOn(play_effects)
  249.    end
  250. +
  251. +  if self:canSoundsBePlayed() then
  252. +    self:tellInterestedEntitiesTheyCanNowPlaySounds()
  253. +  end
  254. +end
  255. +
  256. +function Audio:tellInterestedEntitiesTheyCanNowPlaySounds()
  257. +  if table_length(self.entities_waiting_for_sound_to_be_enabled) > 0 then
  258. +    for entity,callback in pairs(self.entities_waiting_for_sound_to_be_enabled) do
  259. +      callback()
  260. +      self.entities_waiting_for_sound_to_be_enabled[entity] = nil
  261. +    end
  262. +  end
  263. +end
  264. +
  265. +function Audio:entityNoLongerWaitingForSoundsToBeTurnedOn(entity)
  266. +  self.entities_waiting_for_sound_to_be_enabled[entity] = nil
  267.  end
  268.  
  269.  function Audio:setAnnouncementVolume(volume)
  270. diff --git a/CorsixTH/Lua/entity.lua b/CorsixTH/Lua/entity.lua
  271. index fa08ecd..fdbc5c0 100644
  272. --- a/CorsixTH/Lua/entity.lua
  273. +++ b/CorsixTH/Lua/entity.lua
  274. @@ -28,22 +28,57 @@ function Entity:Entity(animation)
  275.    self.layers = {}
  276.    animation:setHitTestResult(self)
  277.    self.ticks = true
  278. +  self.playing_sounds_in_random_sequence = false
  279. +  self.waiting_for_sound_effects_to_be_turned_on = false
  280. +  self.random_sound_sequence_parameters = nil
  281.    self.dynamic_info = nil;
  282.  end
  283.  
  284. --- This plays a sound "at" the entity, meaning the sound will not be played
  285. --- if the entity is off-screen, and the volume will be quieter the further
  286. --- the entity is from the center of the screen. If this is not what you want
  287. --- then use UI:playSound instead.
  288. --- !param name (string, integer) The filename or ordinal of the sound to play.
  289. --- !param played_callback (function) a optional parameter.
  290. --- !param played_callback_delay (integer) a optional milliseconds parameter.
  291. +--[[
  292. +This plays a sound "at" the entity, meaning the sound will not be played
  293. +if the entity is off-screen, and the volume will be quieter the further
  294. +the entity is from the center of the screen. If this is not what you want
  295. +then use UI:playSound instead.
  296. +!param name (string, integer) The filename or ordinal of the sound to play.
  297. +!param played_callback (function) a optional parameter.
  298. +!param played_callback_delay (integer) a optional milliseconds parameter.
  299. +--]]
  300.  function Entity:playSound(name, played_callback, played_callback_delay)
  301.    if TheApp.config.play_sounds then
  302.      TheApp.audio:playSound(name, self, false, played_callback, played_callback_delay)
  303.    end
  304.  end
  305.  
  306. +function Entity:setWaitingForSoundEffectsToBeTurnedOn(state)
  307. +  self.waiting_for_sound_effects_to_be_turned_on = true
  308. +end
  309. +
  310. +--[[
  311. +Plays a sequence of related sounds at an entity while entity.playing_sounds_in_random_sequence = true.
  312. +
  313. +The silences between these sounds can either have randomly generated lengths between the min and max length parameters or
  314. +they can all be a specified length by providing min and max tables with one value for the desired pause duration.
  315. +
  316. +!param name_pattern (String) example: LAVA00*.WAV
  317. +!param min_silence_lengths (table) the desired mininum silences length for the different tick rates, [3] = Normal
  318. +!param max_silence_lengths (table) the desired maximum silences length for the different tick rates, [3] = Normal
  319. +!param num_silences (integer) how many different silence lengths should be used, this can be a nil parameter.
  320. +-]]
  321. +function Entity:playSoundsAtEntityInRandomSequence(name_pattern, min_silence_lengths, max_silence_lengths, num_silences)
  322. +  self.playing_sounds_in_random_sequence = true
  323. +  self.random_sound_sequence_parameters = {}
  324. +  self.random_sound_sequence_parameters["namePattern"] = name_pattern
  325. +  self.random_sound_sequence_parameters["minSilence"] = min_silence_lengths
  326. +  self.random_sound_sequence_parameters["maxSilence"] = max_silence_lengths
  327. +  self.random_sound_sequence_parameters["numSilences"] = num_silences
  328. +
  329. +  TheApp.audio:playSoundsAtEntityInRandomSequence(name_pattern,
  330. +                                                  self,
  331. +                                                  min_silence_lengths,
  332. +                                                  max_silence_lengths,
  333. +                                                  num_silences)
  334. +end
  335. +
  336.  --[[ Set which animation is used to give the entity a visual appearance.
  337.  ! Until an entity is given an animation, it is invisible to the player. Note
  338.  that some "animations" consist of a single frame, and hence the term animation
  339. @@ -234,6 +269,9 @@ function Entity:onDestroy()
  340.    getmetatable(self.gc_dummy).__gc = function()
  341.      print("Entity " .. tostring(self) .. " has been garbage collected.")
  342.    end --]]
  343. +  if self.waiting_for_sound_effects_to_be_turned_on then
  344. +    TheApp.audio:entityNoLongerWaitingForSoundsToBeTurnedOn(self)
  345. +  end
  346.  end
  347.  
  348.  -- Function which is called at the end of each ingame day. Should be used to
  349. @@ -275,6 +313,16 @@ end
  350.  function Entity:afterLoad(old, new)
  351.  end
  352.  
  353. +function Entity:playAfterLoadSound()
  354. +  if self.random_sound_sequence_parameters then
  355. +    self.playing_sounds_in_random_sequence = true
  356. +    self:playSoundsAtEntityInRandomSequence(self.random_sound_sequence_parameters["namePattern"],
  357. +                                            self.random_sound_sequence_parameters["minSilence"],
  358. +                                            self.random_sound_sequence_parameters["maxSilence"],
  359. +                                            self.random_sound_sequence_parameters["numSilences"])
  360. +  end
  361. +end
  362. +
  363.  function Entity:resetAnimation()
  364.    self.th:setDrawingLayer(self:getDrawingLayer())
  365.    local x, y = self.tile_x, self.tile_y
  366. diff --git a/CorsixTH/Lua/game_ui.lua b/CorsixTH/Lua/game_ui.lua
  367. index 6bb7a55..69a4027 100644
  368. --- a/CorsixTH/Lua/game_ui.lua
  369. +++ b/CorsixTH/Lua/game_ui.lua
  370. @@ -725,7 +725,7 @@ end
  371.  
  372.  function UI:togglePlaySounds()
  373.    self.app.config.play_sounds = not self.app.config.play_sounds
  374. -
  375. +  self.app.audio:playSoundEffects(self.app.config.play_sounds)
  376.    self.app:saveConfig()
  377.  end
  378.  
  379. diff --git a/CorsixTH/Lua/world.lua b/CorsixTH/Lua/world.lua
  380. index e17e398..78499fd 100644
  381. --- a/CorsixTH/Lua/world.lua
  382. +++ b/CorsixTH/Lua/world.lua
  383. @@ -949,6 +949,7 @@ function World:setSpeed(speed)
  384.    if self:isCurrentSpeed(speed) then
  385.      return
  386.    end
  387. +  local pause_state_changed = nil
  388.    if speed == "Pause" then
  389.      -- stop screen shaking if there was an earthquake in progress
  390.      if self.active_earthquake then
  391. @@ -956,20 +957,32 @@ function World:setSpeed(speed)
  392.      end
  393.      -- By default actions are not allowed when the game is paused.
  394.      self.user_actions_allowed = TheApp.config.allow_user_actions_while_paused
  395. +    pause_state_changed = true
  396.    elseif self:getCurrentSpeed() == "Pause" then
  397.      self.user_actions_allowed = true
  398.    end
  399. -  
  400. +
  401.    local currentSpeed = self:getCurrentSpeed()
  402.    if currentSpeed ~= "Pause" and currentSpeed ~= "Speed Up" then
  403.      self.prev_speed = self:getCurrentSpeed()
  404.    end
  405.  
  406. +  local was_paused = currentSpeed == "Pause"
  407.    local numerator, denominator = unpack(tick_rates[speed])
  408.    self.hours_per_tick = numerator
  409.    self.tick_rate = denominator
  410. +  
  411. +  if was_paused then
  412. +    TheApp.audio:onEndPause()
  413. +  end
  414. +
  415.    -- Set the blue filter according to whether the user can build or not.
  416.    TheApp.video:setBlueFilterActive(not self.user_actions_allowed)
  417. +  return false
  418. +end
  419. +
  420. +function World:isPaused()
  421. +  return self:isCurrentSpeed("Pause")
  422.  end
  423.  
  424.  --! Dedicated function to allow unpausing by pressing 'p' again
  425. @@ -2358,10 +2371,18 @@ function World:afterLoad(old, new)
  426.    if old < 80 then
  427.      self:determineWinningConditions()
  428.    end
  429. -
  430. +  if old >= 87 then
  431. +    self:playLoadedEntitySounds()
  432. +  end
  433.    self.savegame_version = new
  434.  end
  435.  
  436. +function World:playLoadedEntitySounds()
  437. +  for _, entity in pairs(self.entities) do
  438. +    entity:playAfterLoadSound()
  439. +  end
  440. +end
  441. +
  442.  --[[ There is a problem with room editing in that it resets all the partial passable flags
  443.  (travelNorth, travelSouth etc.) in the corridor, a workaround is calling this function
  444.  after the room was edited so that all edge only objects, that set partial passable flags set

Comments