Loading
#openttdcoop - Paste
Archives
Trending
Docs
Login
ABAP
ActionScript
ActionScript 3
Ada
AIMMS3
ALGOL 68
Apache configuration
AppleScript
Apt sources
ARM ASSEMBLER
ASM
ASP
asymptote
Autoconf
Autohotkey
AutoIt
AviSynth
awk
BASCOM AVR
Bash
Basic4GL
BibTeX
BlitzBasic
bnf
Boo
Brainfuck
C
C#
C (LoadRunner)
C (Mac)
C (WinAPI)
C++
C++ (Qt)
C++ (WinAPI)
CAD DCL
CAD Lisp
CFDG
ChaiScript
Chapel
CIL
Clojure
CMake
COBOL
CoffeeScript
ColdFusion
CSS
Cuesheet
D
Dart
DCL
DCPU-16 Assembly
DCS
Delphi
Diff
DIV
DOS
dot
E
ECMAScript
Eiffel
eMail (mbox)
EPC
Erlang
Euphoria
EZT
F#
Falcon
FO (abas-ERP)
Formula One
Fortran
FreeBasic
FreeSWITCH
GADV 4CS
GAMBAS
GDB
genero
Genie
glSlang
GML
GNU/Octave
GNU Gettext
GNU make
Gnuplot
Go
Groovy
GwBasic
Haskell
Haxe
HicEst
HQ9+
HTML
HTML5
Icon
INI
Inno
INTERCAL
Io
ISPF Panel
J
Java
Java(TM) 2 Platform Standard Edition 5.0
Javascript
JCL
jQuery
KiXtart
KLone C
KLone C++
LaTeX
LDIF
Liberty BASIC
Lisp
LLVM Intermediate Representation
Locomotive Basic
Logtalk
LOLcode
Lotus Notes @Formulas
LotusScript
LScript
LSL2
Lua
MagikSF
MapBasic
Matlab M
Microchip Assembler
Microsoft Registry
mIRC Scripting
MMIX
Modula-2
Modula-3
MOS 6502 (6510) ACME Cross Assembler format
MOS 6502 (6510) Kick Assembler format
MOS 6502 (6510) TASM/64TASS 1.46 Assembler format
Motorola 68000 - HiSoft Devpac ST 2 Assembler format
Motorola 68000 Assembler
MXML
MySQL
Nagios
NetRexx
newlisp
nginx
Nimrod
NML NewGRF Meta Language
NSIS
Oberon-2
Objeck Programming Language
Objective-C
OCaml
OCaml (brief)
ooRexx
OpenBSD Packet Filter
OpenOffice.org Basic
Oracle 8 SQL
Oracle 11 SQL
Oxygene
OZ
ParaSail
PARI/GP
Pascal
PCRE
per
Perl
Perl 6
PHP
PHP (brief)
PIC16
Pike
Pixel Bender 1.0
PL/I
PL/SQL
PostgreSQL
PostScript
POVRAY
PowerBuilder
PowerShell
ProFTPd configuration
Progress
Prolog
PROPERTIES
ProvideX
Puppet
PureBasic
Python
Python for S60
q/kdb+
QBasic/QuickBASIC
QML
R / S+
Racket
Rails
RBScript
REBOL
rexx
robots.txt
RPM Specification File
Ruby
Rust
SAS
Scala
Scheme
SciLab
SCL
sdlBasic
Smalltalk
Smarty
SPARK
SPARQL
SQL
Squirrel Script
Squirrel Script with OpenTTD AI/GS
StandardML
StoneScript
SystemVerilog
T-SQL
TCL
Tera Term Macro
Text
thinBasic
TypoScript
Unicon (Unified Extended Dialect of Icon)
Uno Idl
Unreal Script
UPC
Urbi
Vala
vb.net
VBScript
Vedit macro language
Verilog
VHDL
Vim Script
Visual Basic
Visual Fox Pro
Visual Prolog
Whitespace
Whois (RPSL format)
Winbatch
X++
XBasic
XML
Xorg configuration
YAML
ZiLOG Z80 Assembler
ZXBasic
commit b19ed8d29075ab55ec00b24e8e622f69b1b22939 Author: JASheppard <J_Sheppard@live.co.uk> Date: Thu Mar 13 20:29:44 2014 +0000 New function: playSoundsAtEntityInRandomSequence() 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. Change list ----------- 1. app.lua: a) SAVEGAME_VERSION = 87: Required in this commit because a new afterLoad() sound playing function has been added. b) +afterLoad() call to world:playLoadedEntitySounds() 2. audio.lua: 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. b) +playSoundsAtEntityInRandomSequence() c) +playSoundAtEntityInRandomSequenceRecursionHandler() d) +getRandomSilenceLengths(): Required to provide different durations for the sound seperating pauses in this commit's new audio playing function. 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. f) +canSoundsBePlayed(): Required to support the resumption of sounds being played by this commit's new audio playing function. g) +onEndPause(paused): Required to support the resumption of sounds being played by this commit's new audio playing function. h) +tellInterestedEntitiesTheyCanNowPlaySounds(): Required to support the resumption of sounds being played by this commit's new audio playing function. i) +calls to tellInterestedEntitiesTheyCanNowPlaySounds() 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. 3. entity.lua: a) +Variables for this commit's new audio playing function. b) +playSoundsAtEntityInRandomSequence() c) +playAfterLoadSound(): Required to support the resumption of sounds being played by this commit's new audio playing function. d) +setWaitingForSoundEffectsToBeTurnedOn(state): Required to call audio:entityNoLongerWaitingForSoundsToBeTurnedOn() 4. game_ui.lua: 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. 5. world.lua: a) +Call to audio:onEndPause(): Required to support the resumption of sounds being played by this commit's new audio playing function. b) +isPaused(): Required to support the resumption of sounds being played by this commit's new audio playing function. c) Modified afterLoad() to make it support the resumption of sounds being played by this commit's new audio playing function. d) +playLoadedEntitySounds(): Required to support the resumption of sounds being played by this commit's new audio playing function. diff --git a/CorsixTH/Lua/app.lua b/CorsixTH/Lua/app.lua index e806dd7..e94fcb5 100644 --- a/CorsixTH/Lua/app.lua +++ b/CorsixTH/Lua/app.lua @@ -29,7 +29,7 @@ local assert, io, type, dofile, loadfile, pcall, tonumber, print, setmetatable -- Increment each time a savegame break would occur -- and add compatibility code in afterLoad functions -local SAVEGAME_VERSION = 86 +local SAVEGAME_VERSION = 87 class "App" @@ -1248,6 +1248,7 @@ function App:afterLoad() if new == old then self.world:gameLog("Savegame version is " .. new .. " (" .. self:getVersion() .. "), originally it was " .. first .. " (" .. self:getVersion(first) .. ")") + self.world:playLoadedEntitySounds() return elseif new > old then self.world:gameLog("Savegame version changed from " .. old .. " (" .. self:getVersion(old) .. diff --git a/CorsixTH/Lua/audio.lua b/CorsixTH/Lua/audio.lua index b28c4b1..4aa6dd8 100644 --- a/CorsixTH/Lua/audio.lua +++ b/CorsixTH/Lua/audio.lua @@ -1,4 +1,4 @@ - --[[ Copyright (c) 2009 Peter "Corsix" Cawley +--[[ Copyright (c) 2009 Peter "Corsix" Cawley Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in @@ -36,11 +36,13 @@ function Audio:Audio(app) self.not_loaded = not app.config.audio self.unused_played_callback_id = 0 self.played_sound_callbacks = {} + self.entities_waiting_for_sound_to_be_enabled = {} end function Audio:clearCallbacks() self.unused_played_callback_id = 0 self.played_sound_callbacks = {} + self.entities_waiting_for_sound_to_be_enabled = {} end local function GetFileData(path) @@ -264,18 +266,7 @@ function Audio:playSound(name, where, is_announcement, played_callback, played_c if sound_fx then if name:find("*") then -- Resolve wildcard to one particular sound - local list = wilcard_cache[name] - if not list then - list = {} - wilcard_cache[name] = list - local pattern = ("^" .. name:gsub("%*",".*") .. "$"):upper() - for i = 1, #self.sound_archive - 1 do - local filename = self.sound_archive:getFilename(i):upper() - if filename:find(pattern) then - list[#list + 1] = filename - end - end - end + local list = self:cacheSoundFilenamesAssociatedWithName(name) name = list[1] and list[math.random(1, #list)] or name end local _, warning @@ -303,6 +294,138 @@ function Audio:playSound(name, where, is_announcement, played_callback, played_c end end +function Audio:cacheSoundFilenamesAssociatedWithName(name) + local list = wilcard_cache[name] + if not list then + local filename + list = {} + wilcard_cache[name] = list + local pattern = ("^" .. name:gsub("%*",".*") .. "$"):upper() + for i = 1, #self.sound_archive - 1 do + filename = self.sound_archive:getFilename(i):upper() + if filename:find(pattern) then + list[#list + 1] = filename + end + end + end + return list +end + +--[[ +Plays related sounds at an entity in a random sequence, with random length silences between the sounds. + +This function's integer array parameters for the min and max silence lengths should provide lengths +for this game's different speeds, indexed as follows: +[1] Slowest [2] Slow [3] Normal [4] Fast [5] Maximum Speed + +!param names (string) A name pattern for the sequence of related sounds to be played for example: LAVA00*.wav +!param entity : Where the sounds will be played at, the player won't hear the sounds being played at the entity +when it isn't in their view. +!param min_silence_lengths (integer array) The desired minimum silence lengths for this game's different speeds. +!param max_silence_lengths (integer array) The desired maximum silence lengths for this game's different speeds. +!param num_silences (integer) How many different silence lengths should be used, this can be a nil parameter. +--]] +function Audio:playSoundsAtEntityInRandomSequence(names, entity, min_silence_lengths, max_silence_lengths, num_silences) + if self.sound_fx then + self:cacheSoundFilenamesAssociatedWithName(names) + self:playSoundsAtEntityInRandomSequenceRecursionHandler(wilcard_cache[names], + entity, + self:getRandomSilenceLengths(min_silence_lengths, + max_silence_lengths, + num_silences), + 1) + end +end + +--[[ +Called by the above function. + +This function's integer array parameters for the min and max silence lengths should provide lengths +for this game's different speeds, indexed as follows: +[1] Slowest [2] Slow [3] Normal [4] Fast [5] Maximum Speed + +!param sounds (string) A name pattern for the sequence of related sounds to be played for example: LAVA00*.wav +!param entity : Where the sounds will be played at, the player won't hear the sounds being played at the entity +when it isn't in their view. +!param silences (integer array) the different pause durations to be used between the played sounds. +!param silences_pointer (integer) the index for the pause duration which should be used after this call's sound has been played. +--]] +function Audio:playSoundsAtEntityInRandomSequenceRecursionHandler(sounds, entity, silences, silences_pointer) + if entity.playing_sounds_in_random_sequence then + local sound_played_callback = function() + self:playSoundsAtEntityInRandomSequenceRecursionHandler(sounds, + entity, + silences, + silences_pointer) + end + + if self:canSoundsBePlayed() then + local _, warning + local x, y = Map:WorldToScreen(entity.tile_x, entity.tile_y) + local dx, dy = entity.th:getPosition() + x = x + dx - self.app.ui.screen_offset_x + y = y + dy - self.app.ui.screen_offset_y + + self.played_sound_callbacks[tostring(self.unused_played_callback_id)] = sound_played_callback + _, warning = self.sound_fx:play(sounds[math.random(1,#sounds)], + self.app.config.sound_volume, + x, + y, + self.unused_played_callback_id, + silences_pointer) + + self.unused_played_callback_id = self.unused_played_callback_id + 1 + if #silences > 1 then + silences_pointer = (silences_pointer % #silences) + 1 + end + --If the sound can't be played now: + else + self.entities_waiting_for_sound_to_be_enabled[entity] = sound_played_callback + entity:setWaitingForSoundEffectsToBeTurnedOn(true) + end + else + if self.entities_waiting_for_sound_to_be_enabled[entity] then + self.entities_waiting_for_sound_to_be_enabled[entity] = nil + end + end +end + +function Audio:canSoundsBePlayed() + return TheApp.config.play_sounds and not TheApp.world:isPaused() +end + +--[[ +This function's integer array parameters for the min and max silence lengths should provide lengths +for this game's different speeds, indexed as follows: +[1] Slowest [2] Slow [3] Normal [4] Fast [5] Maximum + +!param min_silence_lengths (integer array) The desired minimum silence lengths for this game's different speeds. +!param max_silence_lengths (integer array) The desired maximum silence lengths for this game's different speeds. +!param num_silences (integer) How many silence lengths should be in the returned table of generated lengths. +!return (table) A table of randomly ordered integers for the generated silence lengths. +--]] +function Audio:getRandomSilenceLengths(min_silence_lengths, max_silence_lengths, num_silences) + local min_silence = min_silence_lengths[TheApp.world.tick_rate] + local max_silence = max_silence_lengths[TheApp.world.tick_rate] + + local silences = {} + if min_silence == max_silence then + silences[1] = min_silence + else + for i = 1, num_silences do + silences[i] = math.random(min_silence,max_silence) + end + end + + return silences +end + +function Audio:onEndPause() + if TheApp.config.play_sounds then + self:tellInterestedEntitiesTheyCanNowPlaySounds() + end +end + function Audio:onSoundPlayed(played_callbacks_id) if TheApp.world ~= nil then if self.played_sound_callbacks[tostring(played_callbacks_id)] then @@ -525,6 +648,23 @@ function Audio:playSoundEffects(play_effects) -- As above. self.sound_fx:setSoundEffectsOn(play_effects) end + + if self:canSoundsBePlayed() then + self:tellInterestedEntitiesTheyCanNowPlaySounds() + end +end + +function Audio:tellInterestedEntitiesTheyCanNowPlaySounds() + if table_length(self.entities_waiting_for_sound_to_be_enabled) > 0 then + for entity,callback in pairs(self.entities_waiting_for_sound_to_be_enabled) do + callback() + self.entities_waiting_for_sound_to_be_enabled[entity] = nil + end + end +end + +function Audio:entityNoLongerWaitingForSoundsToBeTurnedOn(entity) + self.entities_waiting_for_sound_to_be_enabled[entity] = nil end function Audio:setAnnouncementVolume(volume) diff --git a/CorsixTH/Lua/entity.lua b/CorsixTH/Lua/entity.lua index fa08ecd..fdbc5c0 100644 --- a/CorsixTH/Lua/entity.lua +++ b/CorsixTH/Lua/entity.lua @@ -28,22 +28,57 @@ function Entity:Entity(animation) self.layers = {} animation:setHitTestResult(self) self.ticks = true + self.playing_sounds_in_random_sequence = false + self.waiting_for_sound_effects_to_be_turned_on = false + self.random_sound_sequence_parameters = nil self.dynamic_info = nil; end --- This plays a sound "at" the entity, meaning the sound will not be played --- if the entity is off-screen, and the volume will be quieter the further --- the entity is from the center of the screen. If this is not what you want --- then use UI:playSound instead. --- !param name (string, integer) The filename or ordinal of the sound to play. --- !param played_callback (function) a optional parameter. --- !param played_callback_delay (integer) a optional milliseconds parameter. +--[[ +This plays a sound "at" the entity, meaning the sound will not be played +if the entity is off-screen, and the volume will be quieter the further +the entity is from the center of the screen. If this is not what you want +then use UI:playSound instead. +!param name (string, integer) The filename or ordinal of the sound to play. +!param played_callback (function) a optional parameter. +!param played_callback_delay (integer) a optional milliseconds parameter. +--]] function Entity:playSound(name, played_callback, played_callback_delay) if TheApp.config.play_sounds then TheApp.audio:playSound(name, self, false, played_callback, played_callback_delay) end end +function Entity:setWaitingForSoundEffectsToBeTurnedOn(state) + self.waiting_for_sound_effects_to_be_turned_on = true +end + +--[[ +Plays a sequence of related sounds at an entity while entity.playing_sounds_in_random_sequence = true. + +The silences between these sounds can either have randomly generated lengths between the min and max length parameters or +they can all be a specified length by providing min and max tables with one value for the desired pause duration. + +!param name_pattern (String) example: LAVA00*.WAV +!param min_silence_lengths (table) the desired mininum silences length for the different tick rates, [3] = Normal +!param max_silence_lengths (table) the desired maximum silences length for the different tick rates, [3] = Normal +!param num_silences (integer) how many different silence lengths should be used, this can be a nil parameter. +-]] +function Entity:playSoundsAtEntityInRandomSequence(name_pattern, min_silence_lengths, max_silence_lengths, num_silences) + self.playing_sounds_in_random_sequence = true + self.random_sound_sequence_parameters = {} + self.random_sound_sequence_parameters["namePattern"] = name_pattern + self.random_sound_sequence_parameters["minSilence"] = min_silence_lengths + self.random_sound_sequence_parameters["maxSilence"] = max_silence_lengths + self.random_sound_sequence_parameters["numSilences"] = num_silences + + TheApp.audio:playSoundsAtEntityInRandomSequence(name_pattern, + self, + min_silence_lengths, + max_silence_lengths, + num_silences) +end + --[[ Set which animation is used to give the entity a visual appearance. ! Until an entity is given an animation, it is invisible to the player. Note that some "animations" consist of a single frame, and hence the term animation @@ -234,6 +269,9 @@ function Entity:onDestroy() getmetatable(self.gc_dummy).__gc = function() print("Entity " .. tostring(self) .. " has been garbage collected.") end --]] + if self.waiting_for_sound_effects_to_be_turned_on then + TheApp.audio:entityNoLongerWaitingForSoundsToBeTurnedOn(self) + end end -- Function which is called at the end of each ingame day. Should be used to @@ -275,6 +313,16 @@ end function Entity:afterLoad(old, new) end +function Entity:playAfterLoadSound() + if self.random_sound_sequence_parameters then + self.playing_sounds_in_random_sequence = true + self:playSoundsAtEntityInRandomSequence(self.random_sound_sequence_parameters["namePattern"], + self.random_sound_sequence_parameters["minSilence"], + self.random_sound_sequence_parameters["maxSilence"], + self.random_sound_sequence_parameters["numSilences"]) + end +end + function Entity:resetAnimation() self.th:setDrawingLayer(self:getDrawingLayer()) local x, y = self.tile_x, self.tile_y diff --git a/CorsixTH/Lua/game_ui.lua b/CorsixTH/Lua/game_ui.lua index 6bb7a55..69a4027 100644 --- a/CorsixTH/Lua/game_ui.lua +++ b/CorsixTH/Lua/game_ui.lua @@ -725,7 +725,7 @@ end function UI:togglePlaySounds() self.app.config.play_sounds = not self.app.config.play_sounds - + self.app.audio:playSoundEffects(self.app.config.play_sounds) self.app:saveConfig() end diff --git a/CorsixTH/Lua/world.lua b/CorsixTH/Lua/world.lua index e17e398..78499fd 100644 --- a/CorsixTH/Lua/world.lua +++ b/CorsixTH/Lua/world.lua @@ -949,6 +949,7 @@ function World:setSpeed(speed) if self:isCurrentSpeed(speed) then return end + local pause_state_changed = nil if speed == "Pause" then -- stop screen shaking if there was an earthquake in progress if self.active_earthquake then @@ -956,20 +957,32 @@ function World:setSpeed(speed) end -- By default actions are not allowed when the game is paused. self.user_actions_allowed = TheApp.config.allow_user_actions_while_paused + pause_state_changed = true elseif self:getCurrentSpeed() == "Pause" then self.user_actions_allowed = true end - + local currentSpeed = self:getCurrentSpeed() if currentSpeed ~= "Pause" and currentSpeed ~= "Speed Up" then self.prev_speed = self:getCurrentSpeed() end + local was_paused = currentSpeed == "Pause" local numerator, denominator = unpack(tick_rates[speed]) self.hours_per_tick = numerator self.tick_rate = denominator + + if was_paused then + TheApp.audio:onEndPause() + end + -- Set the blue filter according to whether the user can build or not. TheApp.video:setBlueFilterActive(not self.user_actions_allowed) + return false +end + +function World:isPaused() + return self:isCurrentSpeed("Pause") end --! Dedicated function to allow unpausing by pressing 'p' again @@ -2358,10 +2371,18 @@ function World:afterLoad(old, new) if old < 80 then self:determineWinningConditions() end - + if old >= 87 then + self:playLoadedEntitySounds() + end self.savegame_version = new end +function World:playLoadedEntitySounds() + for _, entity in pairs(self.entities) do + entity:playAfterLoadSound() + end +end + --[[ There is a problem with room editing in that it resets all the partial passable flags (travelNorth, travelSouth etc.) in the corridor, a workaround is calling this function after the room was edited so that all edge only objects, that set partial passable flags set
Mark as private
for 30 minutes
for 6 hours
for 1 day
for 1 week
for 1 month
for 1 year
forever