Merge lp:~widelands-dev/widelands/cleanup-soundhandler into lp:widelands

Proposed by GunChleoc
Status: Merged
Merged at revision: 9068
Proposed branch: lp:~widelands-dev/widelands/cleanup-soundhandler
Merge into: lp:widelands
Prerequisite: lp:~widelands-dev/widelands/cleanup-rendertarget
Diff against target: 6413 lines (+1517/-1328)
170 files modified
data/campaigns/emp04.wmf/scripting/tribes/brewery1.lua (+1/-1)
data/campaigns/emp04.wmf/scripting/tribes/brewery2.lua (+1/-1)
data/campaigns/emp04.wmf/scripting/tribes/mill1.lua (+1/-1)
data/campaigns/emp04.wmf/scripting/tribes/mill2.lua (+1/-1)
data/tribes/buildings/productionsites/atlanteans/gold_spinning_mill/init.lua (+1/-1)
data/tribes/buildings/productionsites/atlanteans/horsefarm/init.lua (+1/-1)
data/tribes/buildings/productionsites/atlanteans/mill/init.lua (+2/-2)
data/tribes/buildings/productionsites/atlanteans/sawmill/init.lua (+1/-1)
data/tribes/buildings/productionsites/atlanteans/smelting_works/init.lua (+4/-4)
data/tribes/buildings/productionsites/atlanteans/toolsmithy/init.lua (+12/-12)
data/tribes/buildings/productionsites/atlanteans/weaponsmithy/init.lua (+10/-10)
data/tribes/buildings/productionsites/atlanteans/weaving_mill/init.lua (+3/-3)
data/tribes/buildings/productionsites/barbarians/ax_workshop/init.lua (+6/-6)
data/tribes/buildings/productionsites/barbarians/big_inn/init.lua (+3/-3)
data/tribes/buildings/productionsites/barbarians/cattlefarm/init.lua (+1/-1)
data/tribes/buildings/productionsites/barbarians/inn/init.lua (+2/-2)
data/tribes/buildings/productionsites/barbarians/lime_kiln/init.lua (+2/-2)
data/tribes/buildings/productionsites/barbarians/metal_workshop/init.lua (+10/-10)
data/tribes/buildings/productionsites/barbarians/smelting_works/init.lua (+4/-4)
data/tribes/buildings/productionsites/barbarians/tavern/init.lua (+1/-1)
data/tribes/buildings/productionsites/barbarians/warmill/init.lua (+12/-12)
data/tribes/buildings/productionsites/barbarians/weaving_mill/init.lua (+1/-1)
data/tribes/buildings/productionsites/barbarians/wood_hardener/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/brewery/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/donkeyfarm/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/inn/init.lua (+2/-2)
data/tribes/buildings/productionsites/empire/mill/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/piggery/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/sawmill/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/sheepfarm/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/smelting_works/init.lua (+4/-4)
data/tribes/buildings/productionsites/empire/stonemasons_house/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/tavern/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/toolsmithy/init.lua (+12/-12)
data/tribes/buildings/productionsites/empire/weaponsmithy/init.lua (+10/-10)
data/tribes/buildings/productionsites/empire/weaving_mill/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/winery/init.lua (+1/-1)
data/tribes/buildings/productionsites/frisians/armor_smithy_large/init.lua (+4/-4)
data/tribes/buildings/productionsites/frisians/armor_smithy_small/init.lua (+5/-5)
data/tribes/workers/atlanteans/builder/init.lua (+2/-2)
data/tribes/workers/atlanteans/farmer/init.lua (+1/-1)
data/tribes/workers/atlanteans/fisher/init.lua (+2/-2)
data/tribes/workers/atlanteans/geologist/init.lua (+1/-1)
data/tribes/workers/atlanteans/shipwright/init.lua (+3/-3)
data/tribes/workers/atlanteans/stonecutter/init.lua (+1/-1)
data/tribes/workers/atlanteans/woodcutter/init.lua (+2/-2)
data/tribes/workers/barbarians/builder/init.lua (+2/-2)
data/tribes/workers/barbarians/farmer/init.lua (+1/-1)
data/tribes/workers/barbarians/fisher/init.lua (+2/-2)
data/tribes/workers/barbarians/geologist/init.lua (+1/-1)
data/tribes/workers/barbarians/lumberjack/init.lua (+2/-2)
data/tribes/workers/barbarians/shipwright/init.lua (+3/-3)
data/tribes/workers/barbarians/stonemason/init.lua (+1/-1)
data/tribes/workers/empire/builder/init.lua (+2/-2)
data/tribes/workers/empire/farmer/init.lua (+1/-1)
data/tribes/workers/empire/fisher/init.lua (+2/-2)
data/tribes/workers/empire/geologist/init.lua (+1/-1)
data/tribes/workers/empire/lumberjack/init.lua (+3/-3)
data/tribes/workers/empire/shipwright/init.lua (+3/-3)
data/tribes/workers/empire/stonemason/init.lua (+2/-2)
data/tribes/workers/frisians/builder/init.lua (+2/-2)
data/tribes/workers/frisians/shipwright/init.lua (+2/-2)
data/world/critters/duck/init.lua (+1/-2)
data/world/critters/elk/init.lua (+1/-2)
data/world/critters/fox/init.lua (+1/-2)
data/world/critters/sheep/init.lua (+1/-2)
data/world/critters/stag/init.lua (+1/-2)
data/world/critters/wildboar/init.lua (+1/-2)
data/world/critters/wolf/init.lua (+2/-3)
data/world/immovables/grass1/init.lua (+1/-2)
data/world/immovables/grass2/init.lua (+1/-2)
data/world/immovables/grass3/init.lua (+1/-2)
data/world/immovables/trees/alder/init.lua (+1/-2)
data/world/immovables/trees/aspen/init.lua (+1/-2)
data/world/immovables/trees/beech/init.lua (+1/-2)
data/world/immovables/trees/birch/init.lua (+1/-2)
data/world/immovables/trees/larch/init.lua (+1/-2)
data/world/immovables/trees/maple/init.lua (+1/-2)
data/world/immovables/trees/oak/init.lua (+1/-2)
data/world/immovables/trees/palm_borassus/init.lua (+1/-2)
data/world/immovables/trees/palm_coconut/init.lua (+1/-2)
data/world/immovables/trees/palm_date/init.lua (+1/-2)
data/world/immovables/trees/palm_oil/init.lua (+1/-2)
data/world/immovables/trees/palm_roystonea/init.lua (+1/-2)
data/world/immovables/trees/rowan/init.lua (+1/-2)
data/world/immovables/trees/spruce/init.lua (+1/-2)
doc/sphinx/source/animations.rst (+7/-3)
doc/sphinx/source/productionsite_program.rst (+3/-3)
regression_test.py (+1/-2)
src/economy/flag.h (+1/-0)
src/economy/portdock.cc (+1/-1)
src/economy/portdock.h (+1/-1)
src/economy/road.h (+1/-2)
src/editor/editorinteractive.cc (+6/-2)
src/editor/editorinteractive.h (+1/-0)
src/graphic/CMakeLists.txt (+2/-0)
src/graphic/animation.cc (+26/-17)
src/graphic/animation.h (+7/-1)
src/graphic/game_renderer.cc (+2/-2)
src/graphic/rendertarget.cc (+7/-9)
src/graphic/rendertarget.h (+9/-5)
src/logic/CMakeLists.txt (+1/-0)
src/logic/editor_game_base.cc (+4/-0)
src/logic/game.cc (+3/-2)
src/logic/map_objects/bob.cc (+3/-2)
src/logic/map_objects/bob.h (+1/-1)
src/logic/map_objects/immovable.cc (+9/-8)
src/logic/map_objects/immovable.h (+3/-1)
src/logic/map_objects/immovable_program.h (+4/-5)
src/logic/map_objects/tribes/building.cc (+2/-1)
src/logic/map_objects/tribes/building.h (+1/-1)
src/logic/map_objects/tribes/constructionsite.cc (+16/-6)
src/logic/map_objects/tribes/constructionsite.h (+6/-2)
src/logic/map_objects/tribes/dismantlesite.cc (+13/-3)
src/logic/map_objects/tribes/dismantlesite.h (+5/-0)
src/logic/map_objects/tribes/partially_finished_building.cc (+2/-3)
src/logic/map_objects/tribes/production_program.cc (+10/-8)
src/logic/map_objects/tribes/production_program.h (+1/-1)
src/logic/map_objects/tribes/ship.cc (+4/-3)
src/logic/map_objects/tribes/ship.h (+1/-1)
src/logic/map_objects/tribes/soldier.cc (+3/-3)
src/logic/map_objects/tribes/soldier.h (+1/-0)
src/logic/map_objects/tribes/worker.cc (+8/-7)
src/logic/map_objects/tribes/worker.h (+2/-0)
src/logic/map_objects/tribes/worker_program.cc (+21/-21)
src/logic/message.h (+1/-1)
src/logic/player.cc (+20/-13)
src/logic/player.h (+6/-1)
src/sound/CMakeLists.txt (+8/-0)
src/sound/constants.cc (+1/-0)
src/sound/constants.h (+65/-0)
src/sound/fxset.cc (+76/-23)
src/sound/fxset.h (+32/-35)
src/sound/note_sound.h (+7/-12)
src/sound/songset.cc (+29/-19)
src/sound/songset.h (+5/-9)
src/sound/sound_handler.cc (+388/-443)
src/sound/sound_handler.h (+119/-131)
src/ui_basic/CMakeLists.txt (+1/-0)
src/ui_basic/panel.cc (+10/-7)
src/ui_basic/panel.h (+4/-2)
src/ui_basic/slider.cc (+2/-1)
src/ui_basic/slider.h (+1/-0)
src/ui_fsmenu/CMakeLists.txt (+2/-0)
src/ui_fsmenu/internet_lobby.cc (+4/-1)
src/ui_fsmenu/internet_lobby.h (+1/-0)
src/ui_fsmenu/options.cc (+12/-42)
src/ui_fsmenu/options.h (+4/-8)
src/website/CMakeLists.txt (+0/-1)
src/website/website_common.cc (+1/-7)
src/wlapplication.cc (+41/-13)
src/wlapplication_messages.cc (+0/-4)
src/wui/CMakeLists.txt (+13/-0)
src/wui/game_chat_panel.cc (+5/-2)
src/wui/game_chat_panel.h (+1/-0)
src/wui/game_options_menu.cc (+0/-1)
src/wui/game_options_sound_menu.cc (+19/-119)
src/wui/game_options_sound_menu.h (+5/-40)
src/wui/interactive_base.cc (+29/-23)
src/wui/interactive_base.h (+4/-4)
src/wui/interactive_player.cc (+18/-7)
src/wui/interactive_player.h (+2/-0)
src/wui/interactive_spectator.cc (+7/-2)
src/wui/interactive_spectator.h (+2/-1)
src/wui/itemwaresdisplay.cc (+1/-1)
src/wui/mapview.cc (+4/-3)
src/wui/mapview.h (+1/-1)
src/wui/sound_options.cc (+135/-0)
src/wui/sound_options.h (+32/-0)
src/wui/transport_draw.cc (+5/-5)
To merge this branch: bzr merge lp:~widelands-dev/widelands/cleanup-soundhandler
Reviewer Review Type Date Requested Status
Klaus Halfmann reiew, compile, testplay Approve
Review via email: mp+365001@code.launchpad.net

Commit message

Complete overhaul of the sound handler

- Extensive refactoring of the SoundHandler, FXSet and SongSet classes.
  Make it Widelands-agnostic.
- Categorize sound effects and overhaul the sound options to have
  separate sliders for music, UI sounds, chat messages, game messages
  and ambient sounds. Available both in-game and in the Fullscreen menu
  options.
- Memory saver: Only load sound effects when they are played for the
  first time and then keep them in memory until their category is
  cleared. Clear the ambient sounds when EditorGameBase gets destroyed
- Implement stereo position and distance for sounds emitted by map
  objects. This includes in-game messaages to the player that have been
  triggered by map objects.
- Remove the now obsolete commandline options - we only support
  --nosound now.
- Fix slider style for FSMenu sliders
- Website binaries no longer instantiate the global sound handler.
- Switch to ingame music set in netsetup and internet lobby, because
  just 1 song can get annoying while waiting for other players.
- The register_fx function now only uses the file path for identifying
  the files.
- Randomize the time last played on creation of fxset, to prevent
  ambient sound barrage when starting a new game.
- Rename g_soundhandler to g_sh for consistency with other global
  objects.

Description of the change

This is for Build 21.

Enjoy your games with full stereo sound :)

Unfortunately, panning sound effects that are already playing while the map view is being moved does not seem to work with SDL_Mixer. We'll probably have to switch to a different backend for that (e.g. OpenAL), which is out of scope for this branch.

To post a comment you must log in.
9062. By GunChleoc

Some tweaks from looking at the diff.

9063. By GunChleoc

Remove some blank lines.

Revision history for this message
GunChleoc (gunchleoc) wrote :

The new UI classes and changes to WLApplication are missing from the diff, because it is too long :(

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4628. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/510328711.
Appveyor build 4415. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_cleanup_soundhandler-4415.

9064. By GunChleoc

Merged trunk.

9065. By GunChleoc

Re-added ActPlaySound to ImmovableProgram.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4648. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/511299575.
Appveyor build 4435. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_cleanup_soundhandler-4435.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

I played the first 3 Tutorial on this branhc and everyhting was fine.

When hanging around in the config I get
> Songset: Loaded song "music/menu_00.ogg"
again and again, is this intended, or should this happen only once

I will try to do a code review. But this seems to be a _bigger_ task :-)

Revision history for this message
GunChleoc (gunchleoc) wrote :

That message should come up every time that the song finishes playing and starts again.

Yep, definitely a big code review. Probably best do it with a diff program like Meld and add your remarks as NOCOM comments to the code. I'll merge trunk now to make the diff smaller.

9066. By GunChleoc

Merged trunk.

9067. By GunChleoc

Add missing dependencies to CMakeLists.txt

9068. By GunChleoc

Add more missing dependencies to CMakeLists.txt

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Review, found so far:
* restructered the sound directory wihht more subdirectories
* Help now shows --nosound Starts the game with sound disabled.
* Using FXset one could have different soundscapes for testing or scenarions, interesting
* I assuem the random must be a _local_ random, as sounds are per player.
* More coommetns please, see inline comments

For the wishlist: a helpscreen explainng the sounds :-)

Checekd upto "TODO(GunChleoc): Compatibility code to avoid getting bug reports about unread sections. Remove after Build 21." sorry can nott do more tonight

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

I played some internet game with this brnahc and it was ok, felt a bit slow, though.

Completed the review, code is fine, please check some nits:
 * some functions should be inlined.
 * Once this is in r21 I would like to do a performace review
 * Mabye we could make more of this run in a seperate thread / cpu?

This can go in in r21 anytime now.

review: Approve (reiew, compile, testplay)
Revision history for this message
GunChleoc (gunchleoc) wrote :

Thanks a lot for the review!

SDL_Mixer already uses threading for playing the sounds.

The only other spot where we could use some threading would be when registering sounds, but then the threads would fight over hard disk access and probably make things slower rather than faster. I already have another branch in the works that will speed up finding the files for animations and sounds by not using regular expressions, which will greatly speed up loading the tribes and world.

9069. By GunChleoc

Addressed diff comments.

9070. By GunChleoc

Merged trunk.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4736. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/522063139.
Appveyor build 4521. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_cleanup_soundhandler-4521.

9071. By GunChleoc

Merged trunk.

Revision history for this message
GunChleoc (gunchleoc) wrote :

@bunnybot merge

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4770. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/523599764.
Appveyor build 4554. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_cleanup_soundhandler-4554.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Refusing to merge, since Travis is not green. Use @bunnybot merge force for merging anyways.

Travis build 4770. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/523599764.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Transient failure on Travis

@bunnybot merge force

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/campaigns/emp04.wmf/scripting/tribes/brewery1.lua'
2--- data/campaigns/emp04.wmf/scripting/tribes/brewery1.lua 2018-08-22 06:48:57 +0000
3+++ data/campaigns/emp04.wmf/scripting/tribes/brewery1.lua 2019-04-23 16:25:43 +0000
4@@ -54,7 +54,7 @@
5 "sleep=30000",
6 "return=skipped unless economy needs beer",
7 "consume=water:3 wheat",
8- "playsound=sound/empire beerbubble 180",
9+ "playsound=sound/empire/beerbubble 180",
10 "animate=working 30000",
11 "produce=beer"
12 }
13
14=== modified file 'data/campaigns/emp04.wmf/scripting/tribes/brewery2.lua'
15--- data/campaigns/emp04.wmf/scripting/tribes/brewery2.lua 2018-11-21 09:47:14 +0000
16+++ data/campaigns/emp04.wmf/scripting/tribes/brewery2.lua 2019-04-23 16:25:43 +0000
17@@ -53,7 +53,7 @@
18 "sleep=30000",
19 "return=skipped unless economy needs beer",
20 "consume=water wheat",
21- "playsound=sound/empire beerbubble 180",
22+ "playsound=sound/empire/beerbubble 180",
23 "animate=working 30000",
24 "produce=beer"
25 }
26
27=== modified file 'data/campaigns/emp04.wmf/scripting/tribes/mill1.lua'
28--- data/campaigns/emp04.wmf/scripting/tribes/mill1.lua 2018-08-22 06:48:57 +0000
29+++ data/campaigns/emp04.wmf/scripting/tribes/mill1.lua 2019-04-23 16:25:43 +0000
30@@ -53,7 +53,7 @@
31 "sleep=5000",
32 "return=skipped unless economy needs flour",
33 "consume=wheat:2",
34- "playsound=sound/mill mill_turning 240",
35+ "playsound=sound/mill/mill_turning 240",
36 "animate=working 10000",
37 "produce=flour"
38 }
39
40=== modified file 'data/campaigns/emp04.wmf/scripting/tribes/mill2.lua'
41--- data/campaigns/emp04.wmf/scripting/tribes/mill2.lua 2018-11-21 09:47:14 +0000
42+++ data/campaigns/emp04.wmf/scripting/tribes/mill2.lua 2019-04-23 16:25:43 +0000
43@@ -51,7 +51,7 @@
44 "sleep=5000",
45 "return=skipped unless economy needs flour",
46 "consume=wheat",
47- "playsound=sound/mill mill_turning 240",
48+ "playsound=sound/mill/mill_turning 240",
49 "animate=working 10000",
50 "produce=flour"
51 }
52
53=== modified file 'data/tribes/buildings/productionsites/atlanteans/gold_spinning_mill/init.lua'
54--- data/tribes/buildings/productionsites/atlanteans/gold_spinning_mill/init.lua 2017-11-18 17:57:00 +0000
55+++ data/tribes/buildings/productionsites/atlanteans/gold_spinning_mill/init.lua 2019-04-23 16:25:43 +0000
56@@ -53,7 +53,7 @@
57 "sleep=15000",
58 "return=skipped unless economy needs gold_thread",
59 "consume=gold",
60- "playsound=sound/atlanteans goldspin 192",
61+ "playsound=sound/atlanteans/goldspin 192",
62 "animate=working 25000",
63 "produce=gold_thread"
64 }
65
66=== modified file 'data/tribes/buildings/productionsites/atlanteans/horsefarm/init.lua'
67--- data/tribes/buildings/productionsites/atlanteans/horsefarm/init.lua 2017-11-18 17:57:00 +0000
68+++ data/tribes/buildings/productionsites/atlanteans/horsefarm/init.lua 2019-04-23 16:25:43 +0000
69@@ -55,7 +55,7 @@
70 "sleep=15000",
71 "return=skipped unless economy needs atlanteans_horse",
72 "consume=corn water",
73- "playsound=sound/farm horse 192",
74+ "playsound=sound/farm/horse 192",
75 "animate=working 15000", -- Feeding cute little foals ;)
76 "recruit=atlanteans_horse"
77 }
78
79=== modified file 'data/tribes/buildings/productionsites/atlanteans/mill/init.lua'
80--- data/tribes/buildings/productionsites/atlanteans/mill/init.lua 2019-01-15 21:23:35 +0000
81+++ data/tribes/buildings/productionsites/atlanteans/mill/init.lua 2019-04-23 16:25:43 +0000
82@@ -68,7 +68,7 @@
83 "return=skipped unless economy needs cornmeal",
84 "sleep=3500",
85 "consume=corn",
86- "playsound=sound/mill mill_turning 240",
87+ "playsound=sound/mill/mill_turning 240",
88 "animate=working 15000",
89 "produce=cornmeal"
90 }
91@@ -81,7 +81,7 @@
92 "return=skipped when site has corn and economy needs cornmeal and not economy needs blackroot_flour",
93 "sleep=3500",
94 "consume=blackroot",
95- "playsound=sound/mill mill_turning 240",
96+ "playsound=sound/mill/mill_turning 240",
97 "animate=working 15000",
98 "produce=blackroot_flour"
99 }
100
101=== modified file 'data/tribes/buildings/productionsites/atlanteans/sawmill/init.lua'
102--- data/tribes/buildings/productionsites/atlanteans/sawmill/init.lua 2017-11-18 17:57:00 +0000
103+++ data/tribes/buildings/productionsites/atlanteans/sawmill/init.lua 2019-04-23 16:25:43 +0000
104@@ -55,7 +55,7 @@
105 "sleep=16500", -- Much faster than barbarians' wood hardener
106 "return=skipped unless economy needs planks",
107 "consume=log:2",
108- "playsound=sound/atlanteans/saw benchsaw 192",
109+ "playsound=sound/atlanteans/saw/benchsaw 192",
110 "animate=working 20000", -- Much faster than barbarians' wood hardener
111 "produce=planks"
112 }
113
114=== modified file 'data/tribes/buildings/productionsites/atlanteans/smelting_works/init.lua'
115--- data/tribes/buildings/productionsites/atlanteans/smelting_works/init.lua 2018-09-10 12:32:56 +0000
116+++ data/tribes/buildings/productionsites/atlanteans/smelting_works/init.lua 2019-04-23 16:25:43 +0000
117@@ -68,9 +68,9 @@
118 "return=skipped unless economy needs iron",
119 "consume=iron_ore coal",
120 "sleep=25000",
121- "playsound=sound/metal fizzle 150",
122+ "playsound=sound/metal/fizzle 150",
123 "animate=working 35000",
124- "playsound=sound/metal ironping 80",
125+ "playsound=sound/metal/ironping 80",
126 "produce=iron"
127 }
128 },
129@@ -81,9 +81,9 @@
130 "return=skipped unless economy needs gold",
131 "consume=gold_ore coal",
132 "sleep=25000",
133- "playsound=sound/metal fizzle 150",
134+ "playsound=sound/metal/fizzle 150",
135 "animate=working 35000",
136- "playsound=sound/metal goldping 80",
137+ "playsound=sound/metal/goldping 80",
138 "produce=gold"
139 }
140 },
141
142=== modified file 'data/tribes/buildings/productionsites/atlanteans/toolsmithy/init.lua'
143--- data/tribes/buildings/productionsites/atlanteans/toolsmithy/init.lua 2018-09-10 12:32:56 +0000
144+++ data/tribes/buildings/productionsites/atlanteans/toolsmithy/init.lua 2019-04-23 16:25:43 +0000
145@@ -86,7 +86,7 @@
146 "return=skipped unless economy needs bread_paddle",
147 "consume=iron log",
148 "sleep=32000",
149- "playsound=sound/smiths toolsmith 192",
150+ "playsound=sound/smiths/toolsmith 192",
151 "animate=working 35000",
152 "produce=bread_paddle"
153 }
154@@ -98,7 +98,7 @@
155 "return=skipped unless economy needs buckets",
156 "consume=iron log",
157 "sleep=32000",
158- "playsound=sound/smiths toolsmith 192",
159+ "playsound=sound/smiths/toolsmith 192",
160 "animate=working 35000",
161 "produce=buckets"
162 }
163@@ -110,7 +110,7 @@
164 "return=skipped unless economy needs fire_tongs",
165 "consume=iron log",
166 "sleep=32000",
167- "playsound=sound/smiths toolsmith 192",
168+ "playsound=sound/smiths/toolsmith 192",
169 "animate=working 35000",
170 "produce=fire_tongs"
171 }
172@@ -122,7 +122,7 @@
173 "return=skipped unless economy needs fishing_net",
174 "consume=spidercloth:2",
175 "sleep=32000",
176- "playsound=sound/smiths toolsmith 192",
177+ "playsound=sound/smiths/toolsmith 192",
178 "animate=working 35000",
179 "produce=fishing_net"
180 }
181@@ -134,7 +134,7 @@
182 "return=skipped unless economy needs hammer",
183 "consume=iron log",
184 "sleep=32000",
185- "playsound=sound/smiths toolsmith 192",
186+ "playsound=sound/smiths/toolsmith 192",
187 "animate=working 35000",
188 "produce=hammer"
189 }
190@@ -146,7 +146,7 @@
191 "return=skipped unless economy needs hook_pole",
192 "consume=iron log",
193 "sleep=32000",
194- "playsound=sound/smiths toolsmith 192",
195+ "playsound=sound/smiths/toolsmith 192",
196 "animate=working 35000",
197 "produce=hook_pole"
198 }
199@@ -158,7 +158,7 @@
200 "return=skipped unless economy needs hunting_bow",
201 "consume=log spidercloth",
202 "sleep=32000",
203- "playsound=sound/smiths toolsmith 192",
204+ "playsound=sound/smiths/toolsmith 192",
205 "animate=working 35000",
206 "produce=hunting_bow"
207 }
208@@ -170,7 +170,7 @@
209 "return=skipped unless economy needs milking_tongs",
210 "consume=iron log",
211 "sleep=32000",
212- "playsound=sound/smiths toolsmith 192",
213+ "playsound=sound/smiths/toolsmith 192",
214 "animate=working 35000",
215 "produce=milking_tongs"
216 }
217@@ -182,7 +182,7 @@
218 "return=skipped unless economy needs pick",
219 "consume=iron log",
220 "sleep=32000",
221- "playsound=sound/smiths toolsmith 192",
222+ "playsound=sound/smiths/toolsmith 192",
223 "animate=working 35000",
224 "produce=pick"
225 }
226@@ -194,7 +194,7 @@
227 "return=skipped unless economy needs saw",
228 "consume=iron log",
229 "sleep=32000",
230- "playsound=sound/smiths toolsmith 192",
231+ "playsound=sound/smiths/toolsmith 192",
232 "animate=working 35000",
233 "produce=saw"
234 }
235@@ -206,7 +206,7 @@
236 "return=skipped unless economy needs scythe",
237 "consume=iron log",
238 "sleep=32000",
239- "playsound=sound/smiths toolsmith 192",
240+ "playsound=sound/smiths/toolsmith 192",
241 "animate=working 35000",
242 "produce=scythe"
243 }
244@@ -218,7 +218,7 @@
245 "return=skipped unless economy needs shovel",
246 "consume=iron log",
247 "sleep=32000",
248- "playsound=sound/smiths toolsmith 192",
249+ "playsound=sound/smiths/toolsmith 192",
250 "animate=working 35000",
251 "produce=shovel"
252 }
253
254=== modified file 'data/tribes/buildings/productionsites/atlanteans/weaponsmithy/init.lua'
255--- data/tribes/buildings/productionsites/atlanteans/weaponsmithy/init.lua 2018-09-12 03:04:08 +0000
256+++ data/tribes/buildings/productionsites/atlanteans/weaponsmithy/init.lua 2019-04-23 16:25:43 +0000
257@@ -76,9 +76,9 @@
258 "return=skipped unless economy needs trident_light",
259 "consume=iron planks",
260 "sleep=20000",
261- "playsound=sound/smiths smith 192",
262+ "playsound=sound/smiths/smith 192",
263 "animate=working 21000",
264- "playsound=sound/smiths sharpening 120",
265+ "playsound=sound/smiths/sharpening 120",
266 "sleep=9000",
267 "produce=trident_light"
268 }
269@@ -91,9 +91,9 @@
270 "return=skipped unless economy needs trident_long",
271 "consume=iron coal planks",
272 "sleep=32000",
273- "playsound=sound/smiths smith 192",
274+ "playsound=sound/smiths/smith 192",
275 "animate=working 36000",
276- "playsound=sound/smiths sharpening 120",
277+ "playsound=sound/smiths/sharpening 120",
278 "sleep=9000",
279 "produce=trident_long"
280 }
281@@ -106,9 +106,9 @@
282 "return=skipped unless economy needs trident_steel",
283 "consume=iron:2 coal planks",
284 "sleep=32000",
285- "playsound=sound/smiths smith 192",
286+ "playsound=sound/smiths/smith 192",
287 "animate=working 36000",
288- "playsound=sound/smiths sharpening 120",
289+ "playsound=sound/smiths/sharpening 120",
290 "sleep=9000",
291 "produce=trident_steel"
292 }
293@@ -121,9 +121,9 @@
294 "return=skipped unless economy needs trident_double",
295 "consume=iron coal:2 planks gold",
296 "sleep=32000",
297- "playsound=sound/smiths smith 192",
298+ "playsound=sound/smiths/smith 192",
299 "animate=working 36000",
300- "playsound=sound/smiths sharpening 120",
301+ "playsound=sound/smiths/sharpening 120",
302 "sleep=9000",
303 "produce=trident_double"
304 }
305@@ -136,9 +136,9 @@
306 "return=skipped unless economy needs trident_heavy_double",
307 "consume=iron:2 coal:2 planks gold",
308 "sleep=32000",
309- "playsound=sound/smiths smith 192",
310+ "playsound=sound/smiths/smith 192",
311 "animate=working 36000",
312- "playsound=sound/smiths sharpening 120",
313+ "playsound=sound/smiths/sharpening 120",
314 "sleep=9000",
315 "produce=trident_heavy_double"
316 }
317
318=== modified file 'data/tribes/buildings/productionsites/atlanteans/weaving_mill/init.lua'
319--- data/tribes/buildings/productionsites/atlanteans/weaving_mill/init.lua 2018-09-10 12:32:56 +0000
320+++ data/tribes/buildings/productionsites/atlanteans/weaving_mill/init.lua 2019-04-23 16:25:43 +0000
321@@ -71,7 +71,7 @@
322 "return=skipped unless economy needs spidercloth",
323 "consume=spider_silk",
324 "sleep=20000",
325- "playsound=sound/mill weaving 120",
326+ "playsound=sound/mill/weaving 120",
327 "animate=working 20000",
328 "produce=spidercloth"
329 }
330@@ -84,7 +84,7 @@
331 "return=skipped unless economy needs tabard",
332 "consume=spider_silk",
333 "sleep=20000",
334- "playsound=sound/mill weaving 120",
335+ "playsound=sound/mill/weaving 120",
336 "animate=working 20000",
337 "produce=tabard"
338 }
339@@ -97,7 +97,7 @@
340 "return=skipped unless economy needs tabard_golden",
341 "consume=spider_silk gold_thread",
342 "sleep=20000",
343- "playsound=sound/mill weaving 120",
344+ "playsound=sound/mill/weaving 120",
345 "animate=working 20000",
346 "produce=tabard_golden"
347 }
348
349=== modified file 'data/tribes/buildings/productionsites/barbarians/ax_workshop/init.lua'
350--- data/tribes/buildings/productionsites/barbarians/ax_workshop/init.lua 2018-09-12 03:04:08 +0000
351+++ data/tribes/buildings/productionsites/barbarians/ax_workshop/init.lua 2019-04-23 16:25:43 +0000
352@@ -80,9 +80,9 @@
353 "return=skipped unless economy needs ax",
354 "consume=coal iron",
355 "sleep=26000",
356- "playsound=sound/smiths smith 192",
357+ "playsound=sound/smiths/smith 192",
358 "animate=working 22000",
359- "playsound=sound/smiths sharpening 120",
360+ "playsound=sound/smiths/sharpening 120",
361 "sleep=9000",
362 "produce=ax"
363 }
364@@ -95,9 +95,9 @@
365 "return=skipped unless economy needs ax_sharp",
366 "consume=coal iron:2",
367 "sleep=26000",
368- "playsound=sound/smiths smith 192",
369+ "playsound=sound/smiths/smith 192",
370 "animate=working 22000",
371- "playsound=sound/smiths sharpening 120",
372+ "playsound=sound/smiths/sharpening 120",
373 "sleep=9000",
374 "produce=ax_sharp"
375 }
376@@ -110,9 +110,9 @@
377 "return=skipped unless economy needs ax_broad",
378 "consume=coal:2 iron:2",
379 "sleep=26000",
380- "playsound=sound/smiths smith 192",
381+ "playsound=sound/smiths/smith 192",
382 "animate=working 22000",
383- "playsound=sound/smiths sharpening 120",
384+ "playsound=sound/smiths/sharpening 120",
385 "sleep=9000",
386 "produce=ax_broad"
387 }
388
389=== modified file 'data/tribes/buildings/productionsites/barbarians/big_inn/init.lua'
390--- data/tribes/buildings/productionsites/barbarians/big_inn/init.lua 2018-09-10 12:32:56 +0000
391+++ data/tribes/buildings/productionsites/barbarians/big_inn/init.lua 2019-04-23 16:25:43 +0000
392@@ -73,7 +73,7 @@
393 "return=skipped unless economy needs ration",
394 "sleep=5000",
395 "consume=barbarians_bread,fish,meat",
396- "playsound=sound/barbarians/taverns tavern 100",
397+ "playsound=sound/barbarians/taverns/tavern 100",
398 "animate=working 18000",
399 "sleep=10000",
400 "produce=ration"
401@@ -86,7 +86,7 @@
402 -- time total: 37
403 "return=skipped unless economy needs snack",
404 "consume=barbarians_bread fish,meat beer",
405- "playsound=sound/barbarians/taverns biginn 100",
406+ "playsound=sound/barbarians/taverns/biginn 100",
407 "animate=working 27000",
408 "sleep=10000",
409 "produce=snack"
410@@ -99,7 +99,7 @@
411 -- time total: 40
412 "return=skipped unless economy needs meal",
413 "consume=barbarians_bread fish,meat beer_strong",
414- "playsound=sound/barbarians/taverns biginn 100",
415+ "playsound=sound/barbarians/taverns/biginn 100",
416 "animate=working 30000",
417 "sleep=10000",
418 "produce=meal"
419
420=== modified file 'data/tribes/buildings/productionsites/barbarians/cattlefarm/init.lua'
421--- data/tribes/buildings/productionsites/barbarians/cattlefarm/init.lua 2017-11-18 17:57:00 +0000
422+++ data/tribes/buildings/productionsites/barbarians/cattlefarm/init.lua 2019-04-23 16:25:43 +0000
423@@ -55,7 +55,7 @@
424 "sleep=15000",
425 "return=skipped unless economy needs barbarians_ox",
426 "consume=wheat water",
427- "playsound=sound/farm ox 192",
428+ "playsound=sound/farm/ox 192",
429 "animate=working 15000", -- Animation of feeding the cattle
430 "recruit=barbarians_ox"
431 }
432
433=== modified file 'data/tribes/buildings/productionsites/barbarians/inn/init.lua'
434--- data/tribes/buildings/productionsites/barbarians/inn/init.lua 2018-09-10 12:32:56 +0000
435+++ data/tribes/buildings/productionsites/barbarians/inn/init.lua 2019-04-23 16:25:43 +0000
436@@ -70,7 +70,7 @@
437 "return=skipped unless economy needs ration",
438 "sleep=5000",
439 "consume=barbarians_bread,fish,meat",
440- "playsound=sound/barbarians/taverns inn 100",
441+ "playsound=sound/barbarians/taverns/inn 100",
442 "animate=working 18000",
443 "sleep=10000",
444 "produce=ration"
445@@ -83,7 +83,7 @@
446 -- time total: 37
447 "return=skipped unless economy needs snack",
448 "consume=barbarians_bread fish,meat beer",
449- "playsound=sound/barbarians/taverns inn 100",
450+ "playsound=sound/barbarians/taverns/inn 100",
451 "animate=working 27000",
452 "sleep=10000",
453 "produce=snack"
454
455=== modified file 'data/tribes/buildings/productionsites/barbarians/lime_kiln/init.lua'
456--- data/tribes/buildings/productionsites/barbarians/lime_kiln/init.lua 2017-11-18 17:57:00 +0000
457+++ data/tribes/buildings/productionsites/barbarians/lime_kiln/init.lua 2019-04-23 16:25:43 +0000
458@@ -57,9 +57,9 @@
459 "sleep=50000",
460 "return=skipped unless economy needs grout",
461 "consume=coal granite:2 water:2",
462- "playsound=sound/barbarians stonegrind 100",
463+ "playsound=sound/barbarians/stonegrind 100",
464 "animate=working 29000",
465- "playsound=sound/barbarians mortar 80",
466+ "playsound=sound/barbarians/mortar 80",
467 "sleep=3000",
468 "produce=grout:2"
469 }
470
471=== modified file 'data/tribes/buildings/productionsites/barbarians/metal_workshop/init.lua'
472--- data/tribes/buildings/productionsites/barbarians/metal_workshop/init.lua 2018-09-06 08:21:35 +0000
473+++ data/tribes/buildings/productionsites/barbarians/metal_workshop/init.lua 2019-04-23 16:25:43 +0000
474@@ -96,7 +96,7 @@
475 "return=skipped unless economy needs bread_paddle",
476 "sleep=32000",
477 "consume=iron log",
478- "playsound=sound/smiths toolsmith 192",
479+ "playsound=sound/smiths/toolsmith 192",
480 "animate=working 35000",
481 "produce=bread_paddle"
482 }
483@@ -108,7 +108,7 @@
484 "return=skipped unless economy needs felling_ax",
485 "sleep=32000",
486 "consume=iron log",
487- "playsound=sound/smiths toolsmith 192",
488+ "playsound=sound/smiths/toolsmith 192",
489 "animate=working 35000",
490 "produce=felling_ax"
491 }
492@@ -120,7 +120,7 @@
493 "return=skipped unless economy needs fire_tongs",
494 "sleep=32000",
495 "consume=iron log",
496- "playsound=sound/smiths toolsmith 192",
497+ "playsound=sound/smiths/toolsmith 192",
498 "animate=working 35000",
499 "produce=fire_tongs"
500 }
501@@ -132,7 +132,7 @@
502 "return=skipped unless economy needs fishing_rod",
503 "sleep=32000",
504 "consume=iron log",
505- "playsound=sound/smiths toolsmith 192",
506+ "playsound=sound/smiths/toolsmith 192",
507 "animate=working 35000",
508 "produce=fishing_rod"
509 }
510@@ -144,7 +144,7 @@
511 "return=skipped unless economy needs hammer",
512 "sleep=32000",
513 "consume=iron log",
514- "playsound=sound/smiths toolsmith 192",
515+ "playsound=sound/smiths/toolsmith 192",
516 "animate=working 35000",
517 "produce=hammer"
518 }
519@@ -156,7 +156,7 @@
520 "return=skipped unless economy needs hunting_spear",
521 "sleep=32000",
522 "consume=iron log",
523- "playsound=sound/smiths toolsmith 192",
524+ "playsound=sound/smiths/toolsmith 192",
525 "animate=working 35000",
526 "produce=hunting_spear"
527 }
528@@ -168,7 +168,7 @@
529 "return=skipped unless economy needs kitchen_tools",
530 "sleep=32000",
531 "consume=iron log",
532- "playsound=sound/smiths toolsmith 192",
533+ "playsound=sound/smiths/toolsmith 192",
534 "animate=working 35000",
535 "produce=kitchen_tools"
536 }
537@@ -180,7 +180,7 @@
538 "return=skipped unless economy needs pick",
539 "sleep=32000",
540 "consume=iron log",
541- "playsound=sound/smiths toolsmith 192",
542+ "playsound=sound/smiths/toolsmith 192",
543 "animate=working 35000",
544 "produce=pick"
545 }
546@@ -192,7 +192,7 @@
547 "return=skipped unless economy needs scythe",
548 "sleep=32000",
549 "consume=iron log",
550- "playsound=sound/smiths toolsmith 192",
551+ "playsound=sound/smiths/toolsmith 192",
552 "animate=working 35000",
553 "produce=scythe"
554 }
555@@ -204,7 +204,7 @@
556 "return=skipped unless economy needs shovel",
557 "sleep=32000",
558 "consume=iron log",
559- "playsound=sound/smiths toolsmith 192",
560+ "playsound=sound/smiths/toolsmith 192",
561 "animate=working 35000",
562 "produce=shovel"
563 }
564
565=== modified file 'data/tribes/buildings/productionsites/barbarians/smelting_works/init.lua'
566--- data/tribes/buildings/productionsites/barbarians/smelting_works/init.lua 2018-09-10 12:32:56 +0000
567+++ data/tribes/buildings/productionsites/barbarians/smelting_works/init.lua 2019-04-23 16:25:43 +0000
568@@ -72,9 +72,9 @@
569 "return=skipped unless economy needs iron",
570 "consume=coal iron_ore",
571 "sleep=32000",
572- "playsound=sound/metal furnace 192",
573+ "playsound=sound/metal/furnace 192",
574 "animate=working 35000",
575- "playsound=sound/metal ironping 80",
576+ "playsound=sound/metal/ironping 80",
577 "produce=iron"
578 }
579 },
580@@ -85,9 +85,9 @@
581 "return=skipped unless economy needs gold",
582 "consume=coal gold_ore",
583 "sleep=32000",
584- "playsound=sound/metal furnace 192",
585+ "playsound=sound/metal/furnace 192",
586 "animate=working 35000",
587- "playsound=sound/metal goldping 80",
588+ "playsound=sound/metal/goldping 80",
589 "produce=gold"
590 }
591 },
592
593=== modified file 'data/tribes/buildings/productionsites/barbarians/tavern/init.lua'
594--- data/tribes/buildings/productionsites/barbarians/tavern/init.lua 2018-08-11 14:26:39 +0000
595+++ data/tribes/buildings/productionsites/barbarians/tavern/init.lua 2019-04-23 16:25:43 +0000
596@@ -64,7 +64,7 @@
597 "return=skipped unless economy needs ration",
598 "sleep=5000",
599 "consume=barbarians_bread,fish,meat",
600- "playsound=sound/barbarians/taverns tavern 100",
601+ "playsound=sound/barbarians/taverns/tavern 100",
602 "animate=working 18000",
603 "sleep=10000",
604 "produce=ration"
605
606=== modified file 'data/tribes/buildings/productionsites/barbarians/warmill/init.lua'
607--- data/tribes/buildings/productionsites/barbarians/warmill/init.lua 2018-09-12 03:04:08 +0000
608+++ data/tribes/buildings/productionsites/barbarians/warmill/init.lua 2019-04-23 16:25:43 +0000
609@@ -87,9 +87,9 @@
610 "return=skipped unless economy needs ax",
611 "consume=coal iron",
612 "sleep=26000",
613- "playsound=sound/smiths smith 192",
614+ "playsound=sound/smiths/smith 192",
615 "animate=working 22000",
616- "playsound=sound/smiths sharpening 120",
617+ "playsound=sound/smiths/sharpening 120",
618 "sleep=9000",
619 "produce=ax"
620 }
621@@ -102,9 +102,9 @@
622 "return=skipped unless economy needs ax_sharp",
623 "consume=coal iron:2",
624 "sleep=26000",
625- "playsound=sound/smiths smith 192",
626+ "playsound=sound/smiths/smith 192",
627 "animate=working 22000",
628- "playsound=sound/smiths sharpening 120",
629+ "playsound=sound/smiths/sharpening 120",
630 "sleep=9000",
631 "produce=ax_sharp"
632 }
633@@ -117,9 +117,9 @@
634 "return=skipped unless economy needs ax_broad",
635 "consume=coal:2 iron:2",
636 "sleep=26000",
637- "playsound=sound/smiths smith 192",
638+ "playsound=sound/smiths/smith 192",
639 "animate=working 22000",
640- "playsound=sound/smiths sharpening 120",
641+ "playsound=sound/smiths/sharpening 120",
642 "sleep=9000",
643 "produce=ax_broad"
644 }
645@@ -132,9 +132,9 @@
646 "return=skipped unless economy needs ax_bronze",
647 "consume=coal:2 iron:2",
648 "sleep=26000",
649- "playsound=sound/smiths smith 192",
650+ "playsound=sound/smiths/smith 192",
651 "animate=working 22000",
652- "playsound=sound/smiths sharpening 120",
653+ "playsound=sound/smiths/sharpening 120",
654 "sleep=9000",
655 "produce=ax_bronze"
656 }
657@@ -147,9 +147,9 @@
658 "return=skipped unless economy needs ax_battle",
659 "consume=coal gold iron:2",
660 "sleep=26000",
661- "playsound=sound/smiths smith 192",
662+ "playsound=sound/smiths/smith 192",
663 "animate=working 22000",
664- "playsound=sound/smiths sharpening 120",
665+ "playsound=sound/smiths/sharpening 120",
666 "sleep=9000",
667 "produce=ax_battle"
668 }
669@@ -162,9 +162,9 @@
670 "return=skipped unless economy needs ax_warriors",
671 "consume=coal:2 gold:2 iron:2",
672 "sleep=26000",
673- "playsound=sound/smiths smith 192",
674+ "playsound=sound/smiths/smith 192",
675 "animate=working 22000",
676- "playsound=sound/smiths sharpening 120",
677+ "playsound=sound/smiths/sharpening 120",
678 "sleep=9000",
679 "produce=ax_warriors"
680 }
681
682=== modified file 'data/tribes/buildings/productionsites/barbarians/weaving_mill/init.lua'
683--- data/tribes/buildings/productionsites/barbarians/weaving_mill/init.lua 2017-11-18 21:23:09 +0000
684+++ data/tribes/buildings/productionsites/barbarians/weaving_mill/init.lua 2019-04-23 16:25:43 +0000
685@@ -59,7 +59,7 @@
686 "checkmap=seafaring",
687 "return=skipped unless economy needs cloth",
688 "consume=thatch_reed",
689- "playsound=sound/barbarians weaver 120",
690+ "playsound=sound/barbarians/weaver 120",
691 "animate=working 20000",
692 "produce=cloth"
693 }
694
695=== modified file 'data/tribes/buildings/productionsites/barbarians/wood_hardener/init.lua'
696--- data/tribes/buildings/productionsites/barbarians/wood_hardener/init.lua 2018-09-10 12:32:56 +0000
697+++ data/tribes/buildings/productionsites/barbarians/wood_hardener/init.lua 2019-04-23 16:25:43 +0000
698@@ -62,7 +62,7 @@
699 "sleep=43000",
700 "return=skipped unless economy needs blackwood",
701 "consume=log:2",
702- "playsound=sound/barbarians blackwood 80",
703+ "playsound=sound/barbarians/blackwood 80",
704 "animate=working 24000",
705 "produce=blackwood"
706 }
707
708=== modified file 'data/tribes/buildings/productionsites/empire/brewery/init.lua'
709--- data/tribes/buildings/productionsites/empire/brewery/init.lua 2017-11-18 17:57:00 +0000
710+++ data/tribes/buildings/productionsites/empire/brewery/init.lua 2019-04-23 16:25:43 +0000
711@@ -56,7 +56,7 @@
712 "sleep=30000",
713 "return=skipped unless economy needs beer",
714 "consume=water wheat",
715- "playsound=sound/empire beerbubble 180",
716+ "playsound=sound/empire/beerbubble 180",
717 "animate=working 30000",
718 "produce=beer"
719 }
720
721=== modified file 'data/tribes/buildings/productionsites/empire/donkeyfarm/init.lua'
722--- data/tribes/buildings/productionsites/empire/donkeyfarm/init.lua 2017-11-18 17:57:00 +0000
723+++ data/tribes/buildings/productionsites/empire/donkeyfarm/init.lua 2019-04-23 16:25:43 +0000
724@@ -55,7 +55,7 @@
725 "sleep=15000",
726 "return=skipped unless economy needs empire_donkey",
727 "consume=wheat water",
728- "playsound=sound/farm donkey 192",
729+ "playsound=sound/farm/donkey 192",
730 "animate=working 15000", -- Feeding cute little baby donkeys ;)
731 "recruit=empire_donkey"
732 }
733
734=== modified file 'data/tribes/buildings/productionsites/empire/inn/init.lua'
735--- data/tribes/buildings/productionsites/empire/inn/init.lua 2018-09-10 12:32:56 +0000
736+++ data/tribes/buildings/productionsites/empire/inn/init.lua 2019-04-23 16:25:43 +0000
737@@ -66,7 +66,7 @@
738 "return=skipped unless economy needs ration",
739 "sleep=5000",
740 "consume=empire_bread,fish,meat",
741- "playsound=sound/empire/taverns ration 100",
742+ "playsound=sound/empire/taverns/ration 100",
743 "animate=working 18000",
744 "sleep=10000",
745 "produce=ration"
746@@ -79,7 +79,7 @@
747 -- time total: 40
748 "return=skipped unless economy needs meal",
749 "consume=empire_bread fish,meat",
750- "playsound=sound/empire/taverns meal 100",
751+ "playsound=sound/empire/taverns/meal 100",
752 "animate=working 30000",
753 "sleep=10000",
754 "produce=meal"
755
756=== modified file 'data/tribes/buildings/productionsites/empire/mill/init.lua'
757--- data/tribes/buildings/productionsites/empire/mill/init.lua 2017-11-18 17:57:00 +0000
758+++ data/tribes/buildings/productionsites/empire/mill/init.lua 2019-04-23 16:25:43 +0000
759@@ -55,7 +55,7 @@
760 "sleep=5000",
761 "return=skipped unless economy needs flour",
762 "consume=wheat",
763- "playsound=sound/mill mill_turning 240",
764+ "playsound=sound/mill/mill_turning 240",
765 "animate=working 10000",
766 "produce=flour"
767 }
768
769=== modified file 'data/tribes/buildings/productionsites/empire/piggery/init.lua'
770--- data/tribes/buildings/productionsites/empire/piggery/init.lua 2017-11-18 17:57:00 +0000
771+++ data/tribes/buildings/productionsites/empire/piggery/init.lua 2019-04-23 16:25:43 +0000
772@@ -56,7 +56,7 @@
773 "sleep=25000",
774 "return=skipped unless economy needs meat",
775 "consume=water wheat",
776- "playsound=sound/farm farm_animal 180",
777+ "playsound=sound/farm/farm_animal 180",
778 "animate=working 30000",
779 "produce=meat"
780 }
781
782=== modified file 'data/tribes/buildings/productionsites/empire/sawmill/init.lua'
783--- data/tribes/buildings/productionsites/empire/sawmill/init.lua 2017-11-18 17:57:00 +0000
784+++ data/tribes/buildings/productionsites/empire/sawmill/init.lua 2019-04-23 16:25:43 +0000
785@@ -55,7 +55,7 @@
786 "sleep=16500", -- Much faster than barbarians' wood hardener
787 "return=skipped unless economy needs planks",
788 "consume=log:2",
789- "playsound=sound/sawmill sawmill 180",
790+ "playsound=sound/sawmill/sawmill 180",
791 "animate=working 20000", -- Much faster than barbarians' wood hardener
792 "produce=planks"
793 }
794
795=== modified file 'data/tribes/buildings/productionsites/empire/sheepfarm/init.lua'
796--- data/tribes/buildings/productionsites/empire/sheepfarm/init.lua 2017-11-18 17:57:00 +0000
797+++ data/tribes/buildings/productionsites/empire/sheepfarm/init.lua 2019-04-23 16:25:43 +0000
798@@ -56,7 +56,7 @@
799 "sleep=25000",
800 "return=skipped unless economy needs wool",
801 "consume=water wheat",
802- "playsound=sound/farm sheep 192",
803+ "playsound=sound/farm/sheep 192",
804 "animate=working 30000",
805 "produce=wool"
806 }
807
808=== modified file 'data/tribes/buildings/productionsites/empire/smelting_works/init.lua'
809--- data/tribes/buildings/productionsites/empire/smelting_works/init.lua 2018-09-10 12:32:56 +0000
810+++ data/tribes/buildings/productionsites/empire/smelting_works/init.lua 2019-04-23 16:25:43 +0000
811@@ -73,9 +73,9 @@
812 "return=skipped unless economy needs iron",
813 "consume=iron_ore coal",
814 "sleep=25000",
815- "playsound=sound/metal fizzle 150",
816+ "playsound=sound/metal/fizzle 150",
817 "animate=working 35000",
818- "playsound=sound/metal ironping 80",
819+ "playsound=sound/metal/ironping 80",
820 "produce=iron"
821 }
822 },
823@@ -86,9 +86,9 @@
824 "return=skipped unless economy needs gold",
825 "consume=gold_ore coal",
826 "sleep=25000",
827- "playsound=sound/metal fizzle 150",
828+ "playsound=sound/metal/fizzle 150",
829 "animate=working 35000",
830- "playsound=sound/metal goldping 80",
831+ "playsound=sound/metal/goldping 80",
832 "produce=gold"
833 }
834 },
835
836=== modified file 'data/tribes/buildings/productionsites/empire/stonemasons_house/init.lua'
837--- data/tribes/buildings/productionsites/empire/stonemasons_house/init.lua 2017-11-18 17:57:00 +0000
838+++ data/tribes/buildings/productionsites/empire/stonemasons_house/init.lua 2019-04-23 16:25:43 +0000
839@@ -56,7 +56,7 @@
840 "sleep=50000",
841 "return=skipped unless economy needs marble_column",
842 "consume=marble:2",
843- "playsound=sound/stonecutting stonemason 192",
844+ "playsound=sound/stonecutting/stonemason 192",
845 "animate=working 32000",
846 "produce=marble_column"
847 }
848
849=== modified file 'data/tribes/buildings/productionsites/empire/tavern/init.lua'
850--- data/tribes/buildings/productionsites/empire/tavern/init.lua 2018-08-11 14:26:39 +0000
851+++ data/tribes/buildings/productionsites/empire/tavern/init.lua 2019-04-23 16:25:43 +0000
852@@ -59,7 +59,7 @@
853 "return=skipped unless economy needs ration",
854 "sleep=5000",
855 "consume=empire_bread,fish,meat",
856- "playsound=sound/empire/taverns ration 100",
857+ "playsound=sound/empire/taverns/ration 100",
858 "animate=working 18000",
859 "sleep=10000",
860 "produce=ration"
861
862=== modified file 'data/tribes/buildings/productionsites/empire/toolsmithy/init.lua'
863--- data/tribes/buildings/productionsites/empire/toolsmithy/init.lua 2018-09-06 08:21:35 +0000
864+++ data/tribes/buildings/productionsites/empire/toolsmithy/init.lua 2019-04-23 16:25:43 +0000
865@@ -85,7 +85,7 @@
866 "return=skipped unless economy needs felling_ax",
867 "sleep=32000",
868 "consume=iron log",
869- "playsound=sound/smiths toolsmith 192",
870+ "playsound=sound/smiths/toolsmith 192",
871 "animate=working 35000",
872 "produce=felling_ax"
873 }
874@@ -97,7 +97,7 @@
875 "return=skipped unless economy needs basket",
876 "sleep=32000",
877 "consume=iron log",
878- "playsound=sound/smiths toolsmith 192",
879+ "playsound=sound/smiths/toolsmith 192",
880 "animate=working 35000",
881 "produce=basket"
882 }
883@@ -109,7 +109,7 @@
884 "return=skipped unless economy needs bread_paddle",
885 "sleep=32000",
886 "consume=iron log",
887- "playsound=sound/smiths toolsmith 192",
888+ "playsound=sound/smiths/toolsmith 192",
889 "animate=working 35000",
890 "produce=bread_paddle"
891 }
892@@ -121,7 +121,7 @@
893 "return=skipped unless economy needs fire_tongs",
894 "sleep=32000",
895 "consume=iron log",
896- "playsound=sound/smiths toolsmith 192",
897+ "playsound=sound/smiths/toolsmith 192",
898 "animate=working 35000",
899 "produce=fire_tongs"
900 }
901@@ -133,7 +133,7 @@
902 "return=skipped unless economy needs fishing_rod",
903 "sleep=32000",
904 "consume=iron log",
905- "playsound=sound/smiths toolsmith 192",
906+ "playsound=sound/smiths/toolsmith 192",
907 "animate=working 35000",
908 "produce=fishing_rod"
909 }
910@@ -145,7 +145,7 @@
911 "return=skipped unless economy needs hammer",
912 "sleep=32000",
913 "consume=iron log",
914- "playsound=sound/smiths toolsmith 192",
915+ "playsound=sound/smiths/toolsmith 192",
916 "animate=working 35000",
917 "produce=hammer"
918 }
919@@ -157,7 +157,7 @@
920 "return=skipped unless economy needs hunting_spear",
921 "sleep=32000",
922 "consume=iron log",
923- "playsound=sound/smiths toolsmith 192",
924+ "playsound=sound/smiths/toolsmith 192",
925 "animate=working 35000",
926 "produce=hunting_spear"
927 }
928@@ -169,7 +169,7 @@
929 "return=skipped unless economy needs kitchen_tools",
930 "sleep=32000",
931 "consume=iron log",
932- "playsound=sound/smiths toolsmith 192",
933+ "playsound=sound/smiths/toolsmith 192",
934 "animate=working 35000",
935 "produce=kitchen_tools"
936 }
937@@ -181,7 +181,7 @@
938 "return=skipped unless economy needs pick",
939 "sleep=32000",
940 "consume=iron log",
941- "playsound=sound/smiths toolsmith 192",
942+ "playsound=sound/smiths/toolsmith 192",
943 "animate=working 35000",
944 "produce=pick"
945 }
946@@ -193,7 +193,7 @@
947 "return=skipped unless economy needs saw",
948 "sleep=32000",
949 "consume=iron log",
950- "playsound=sound/smiths toolsmith 192",
951+ "playsound=sound/smiths/toolsmith 192",
952 "animate=working 35000",
953 "produce=saw"
954 }
955@@ -205,7 +205,7 @@
956 "return=skipped unless economy needs scythe",
957 "sleep=32000",
958 "consume=iron log",
959- "playsound=sound/smiths toolsmith 192",
960+ "playsound=sound/smiths/toolsmith 192",
961 "animate=working 35000",
962 "produce=scythe"
963 }
964@@ -217,7 +217,7 @@
965 "return=skipped unless economy needs shovel",
966 "sleep=32000",
967 "consume=iron log",
968- "playsound=sound/smiths toolsmith 192",
969+ "playsound=sound/smiths/toolsmith 192",
970 "animate=working 35000",
971 "produce=shovel"
972 }
973
974=== modified file 'data/tribes/buildings/productionsites/empire/weaponsmithy/init.lua'
975--- data/tribes/buildings/productionsites/empire/weaponsmithy/init.lua 2018-09-12 03:04:08 +0000
976+++ data/tribes/buildings/productionsites/empire/weaponsmithy/init.lua 2019-04-23 16:25:43 +0000
977@@ -81,9 +81,9 @@
978 "return=skipped unless economy needs spear_wooden",
979 "consume=planks",
980 "sleep=20000",
981- "playsound=sound/smiths smith 192",
982+ "playsound=sound/smiths/smith 192",
983 "animate=working 21000",
984- "playsound=sound/smiths sharpening 120",
985+ "playsound=sound/smiths/sharpening 120",
986 "sleep=9000",
987 "produce=spear_wooden"
988 }
989@@ -96,9 +96,9 @@
990 "return=skipped unless economy needs spear",
991 "consume=coal iron planks",
992 "sleep=32000",
993- "playsound=sound/smiths smith 192",
994+ "playsound=sound/smiths/smith 192",
995 "animate=working 36000",
996- "playsound=sound/smiths sharpening 120",
997+ "playsound=sound/smiths/sharpening 120",
998 "sleep=9000",
999 "produce=spear"
1000 }
1001@@ -111,9 +111,9 @@
1002 "return=skipped unless economy needs spear_advanced",
1003 "consume=coal iron:2 planks",
1004 "sleep=32000",
1005- "playsound=sound/smiths smith 192",
1006+ "playsound=sound/smiths/smith 192",
1007 "animate=working 36000",
1008- "playsound=sound/smiths sharpening 120",
1009+ "playsound=sound/smiths/sharpening 120",
1010 "sleep=9000",
1011 "produce=spear_advanced"
1012 }
1013@@ -126,9 +126,9 @@
1014 "return=skipped unless economy needs spear_heavy",
1015 "consume=coal:2 gold iron planks",
1016 "sleep=32000",
1017- "playsound=sound/smiths smith 192",
1018+ "playsound=sound/smiths/smith 192",
1019 "animate=working 36000",
1020- "playsound=sound/smiths sharpening 120",
1021+ "playsound=sound/smiths/sharpening 120",
1022 "sleep=9000",
1023 "produce=spear_heavy"
1024 }
1025@@ -141,9 +141,9 @@
1026 "return=skipped unless economy needs spear_war",
1027 "consume=coal:2 gold iron:2 planks",
1028 "sleep=32000",
1029- "playsound=sound/smiths smith 192",
1030+ "playsound=sound/smiths/smith 192",
1031 "animate=working 36000",
1032- "playsound=sound/smiths sharpening 120",
1033+ "playsound=sound/smiths/sharpening 120",
1034 "sleep=9000",
1035 "produce=spear_war"
1036 }
1037
1038=== modified file 'data/tribes/buildings/productionsites/empire/weaving_mill/init.lua'
1039--- data/tribes/buildings/productionsites/empire/weaving_mill/init.lua 2017-11-18 17:57:00 +0000
1040+++ data/tribes/buildings/productionsites/empire/weaving_mill/init.lua 2019-04-23 16:25:43 +0000
1041@@ -63,7 +63,7 @@
1042 "return=skipped unless economy needs cloth",
1043 "consume=wool",
1044 "sleep=15000",
1045- "playsound=sound/mill weaving 120",
1046+ "playsound=sound/mill/weaving 120",
1047 "animate=working 15000", -- Unsure of balancing CW
1048 "sleep=5000",
1049 "produce=cloth"
1050
1051=== modified file 'data/tribes/buildings/productionsites/empire/winery/init.lua'
1052--- data/tribes/buildings/productionsites/empire/winery/init.lua 2017-11-18 17:57:00 +0000
1053+++ data/tribes/buildings/productionsites/empire/winery/init.lua 2019-04-23 16:25:43 +0000
1054@@ -58,7 +58,7 @@
1055 -- Grapes are only needed for wine, so no need to check if wine is needed
1056 "sleep=30000",
1057 "consume=grape:2",
1058- "playsound=sound/empire winebubble 180",
1059+ "playsound=sound/empire/winebubble 180",
1060 "animate=working 30000",
1061 "produce=wine"
1062 }
1063
1064=== modified file 'data/tribes/buildings/productionsites/frisians/armor_smithy_large/init.lua'
1065--- data/tribes/buildings/productionsites/frisians/armor_smithy_large/init.lua 2018-09-10 12:32:56 +0000
1066+++ data/tribes/buildings/productionsites/frisians/armor_smithy_large/init.lua 2019-04-23 16:25:43 +0000
1067@@ -78,9 +78,9 @@
1068 "return=skipped unless economy needs sword_broad",
1069 "consume=coal iron:2 gold",
1070 "sleep=24000",
1071- "playsound=sound/smiths smith 192",
1072+ "playsound=sound/smiths/smith 192",
1073 "animate=working 24000",
1074- "playsound=sound/smiths sharpening 120",
1075+ "playsound=sound/smiths/sharpening 120",
1076 "sleep=9000",
1077 "produce=sword_broad"
1078 },
1079@@ -93,9 +93,9 @@
1080 "return=skipped unless economy needs sword_double",
1081 "consume=coal:2 iron:2 gold",
1082 "sleep=24000",
1083- "playsound=sound/smiths smith 192",
1084+ "playsound=sound/smiths/smith 192",
1085 "animate=working 24000",
1086- "playsound=sound/smiths sharpening 120",
1087+ "playsound=sound/smiths/sharpening 120",
1088 "sleep=9000",
1089 "produce=sword_double"
1090 },
1091
1092=== modified file 'data/tribes/buildings/productionsites/frisians/armor_smithy_small/init.lua'
1093--- data/tribes/buildings/productionsites/frisians/armor_smithy_small/init.lua 2018-09-16 13:19:53 +0000
1094+++ data/tribes/buildings/productionsites/frisians/armor_smithy_small/init.lua 2019-04-23 16:25:43 +0000
1095@@ -77,9 +77,9 @@
1096 "return=skipped unless economy needs sword_short",
1097 "consume=coal iron",
1098 "sleep=24000",
1099- "playsound=sound/smiths smith 192",
1100+ "playsound=sound/smiths/smith 192",
1101 "animate=working 24000",
1102- "playsound=sound/smiths sharpening 120",
1103+ "playsound=sound/smiths/sharpening 120",
1104 "sleep=9000",
1105 "produce=sword_short"
1106 },
1107@@ -92,9 +92,9 @@
1108 "return=skipped unless economy needs sword_long",
1109 "consume=coal iron:2",
1110 "sleep=24000",
1111- "playsound=sound/smiths smith 192",
1112+ "playsound=sound/smiths/smith 192",
1113 "animate=working 24000",
1114- "playsound=sound/smiths sharpening 120",
1115+ "playsound=sound/smiths/sharpening 120",
1116 "sleep=9000",
1117 "produce=sword_long"
1118 },
1119@@ -107,7 +107,7 @@
1120 "return=skipped unless economy needs helmet",
1121 "consume=coal iron",
1122 "sleep=30000",
1123- "playsound=sound/smiths smith 192",
1124+ "playsound=sound/smiths/smith 192",
1125 "animate=working 37000",
1126 "produce=helmet"
1127 },
1128
1129=== modified file 'data/tribes/workers/atlanteans/builder/init.lua'
1130--- data/tribes/workers/atlanteans/builder/init.lua 2017-02-12 09:10:57 +0000
1131+++ data/tribes/workers/atlanteans/builder/init.lua 2019-04-23 16:25:43 +0000
1132@@ -9,8 +9,8 @@
1133 work = {
1134 pictures = path.list_files(dirname .. "work_??.png"),
1135 sound_effect = {
1136- directory = "sound/hammering",
1137- name = "hammering",
1138+ path = "sound/hammering/hammering",
1139+ priority = 64
1140 },
1141 hotspot = { 6, 22 },
1142 fps=10,
1143
1144=== modified file 'data/tribes/workers/atlanteans/farmer/init.lua'
1145--- data/tribes/workers/atlanteans/farmer/init.lua 2018-02-28 09:38:13 +0000
1146+++ data/tribes/workers/atlanteans/farmer/init.lua 2019-04-23 16:25:43 +0000
1147@@ -51,7 +51,7 @@
1148 harvest = {
1149 "findobject=attrib:ripe_corn radius:2",
1150 "walk=object",
1151- "playsound=sound/farm scythe 220",
1152+ "playsound=sound/farm/scythe 220",
1153 "animate=harvesting 10000",
1154 "callobject=harvest",
1155 "animate=gathering 4000",
1156
1157=== modified file 'data/tribes/workers/atlanteans/fisher/init.lua'
1158--- data/tribes/workers/atlanteans/fisher/init.lua 2017-11-18 17:57:00 +0000
1159+++ data/tribes/workers/atlanteans/fisher/init.lua 2019-04-23 16:25:43 +0000
1160@@ -33,10 +33,10 @@
1161 fish = {
1162 "findspace=size:any radius:7 resource:fish",
1163 "walk=coords",
1164- "playsound=sound/fisher fisher_throw_net 192",
1165+ "playsound=sound/fisher/fisher_throw_net 192",
1166 "mine=fish 1",
1167 "animate=fishing 3000",
1168- "playsound=sound/fisher fisher_pull_net 192",
1169+ "playsound=sound/fisher/fisher_pull_net 192",
1170 "createware=fish",
1171 "return"
1172 }
1173
1174=== modified file 'data/tribes/workers/atlanteans/geologist/init.lua'
1175--- data/tribes/workers/atlanteans/geologist/init.lua 2018-07-08 18:32:58 +0000
1176+++ data/tribes/workers/atlanteans/geologist/init.lua 2019-04-23 16:25:43 +0000
1177@@ -41,7 +41,7 @@
1178 search = {
1179 "animate=hacking 5000",
1180 "animate=idle 2000",
1181- "playsound=sound/hammering geologist_hammer 192",
1182+ "playsound=sound/hammering/geologist_hammer 192",
1183 "animate=hacking 3000",
1184 "findresources"
1185 }
1186
1187=== modified file 'data/tribes/workers/atlanteans/shipwright/init.lua'
1188--- data/tribes/workers/atlanteans/shipwright/init.lua 2018-02-28 09:38:13 +0000
1189+++ data/tribes/workers/atlanteans/shipwright/init.lua 2019-04-23 16:25:43 +0000
1190@@ -4,8 +4,8 @@
1191 idle = {
1192 pictures = path.list_files(dirname .. "idle_??.png"),
1193 sound_effect = {
1194- directory = "sound/hammering",
1195- name = "hammering",
1196+ path = "sound/hammering/hammering",
1197+ priority = 64
1198 },
1199 hotspot = { 12, 28 },
1200 fps = 10
1201@@ -33,7 +33,7 @@
1202 buildship = {
1203 "walk=object-or-coords",
1204 "plant=attrib:shipconstruction unless object",
1205- "playsound=sound/sawmill sawmill 230",
1206+ "playsound=sound/sawmill/sawmill 230",
1207 "animate=idle 500",
1208 "construct",
1209 "animate=idle 5000",
1210
1211=== modified file 'data/tribes/workers/atlanteans/stonecutter/init.lua'
1212--- data/tribes/workers/atlanteans/stonecutter/init.lua 2017-11-18 17:57:00 +0000
1213+++ data/tribes/workers/atlanteans/stonecutter/init.lua 2019-04-23 16:25:43 +0000
1214@@ -33,7 +33,7 @@
1215 cut_granite = {
1216 "findobject=attrib:rocks radius:6",
1217 "walk=object",
1218- "playsound=sound/atlanteans/cutting stonecutter 192",
1219+ "playsound=sound/atlanteans/cutting/stonecutter 192",
1220 "animate=hacking 12000",
1221 "callobject=shrink",
1222 "createware=granite",
1223
1224=== modified file 'data/tribes/workers/atlanteans/woodcutter/init.lua'
1225--- data/tribes/workers/atlanteans/woodcutter/init.lua 2017-11-18 17:57:00 +0000
1226+++ data/tribes/workers/atlanteans/woodcutter/init.lua 2019-04-23 16:25:43 +0000
1227@@ -33,9 +33,9 @@
1228 harvest = {
1229 "findobject=attrib:tree radius:10",
1230 "walk=object",
1231- "playsound=sound/atlanteans/saw sawing 230",
1232+ "playsound=sound/atlanteans/saw/sawing 230",
1233 "animate=sawing 10000",
1234- "playsound=sound/woodcutting tree-falling 130",
1235+ "playsound=sound/woodcutting/tree-falling 130",
1236 "callobject=fall",
1237 "animate=idle 2000",
1238 "createware=log",
1239
1240=== modified file 'data/tribes/workers/barbarians/builder/init.lua'
1241--- data/tribes/workers/barbarians/builder/init.lua 2017-02-12 09:10:57 +0000
1242+++ data/tribes/workers/barbarians/builder/init.lua 2019-04-23 16:25:43 +0000
1243@@ -9,8 +9,8 @@
1244 work = {
1245 pictures = path.list_files(dirname .. "work_??.png"),
1246 sound_effect = {
1247- directory = "sound/hammering",
1248- name = "hammering",
1249+ path = "sound/hammering/hammering",
1250+ priority = 64
1251 },
1252 hotspot = { 10, 22 },
1253 fps = 10
1254
1255=== modified file 'data/tribes/workers/barbarians/farmer/init.lua'
1256--- data/tribes/workers/barbarians/farmer/init.lua 2018-02-28 09:38:13 +0000
1257+++ data/tribes/workers/barbarians/farmer/init.lua 2019-04-23 16:25:43 +0000
1258@@ -51,7 +51,7 @@
1259 harvest = {
1260 "findobject=attrib:ripe_wheat radius:2",
1261 "walk=object",
1262- "playsound=sound/farm scythe 220",
1263+ "playsound=sound/farm/scythe 220",
1264 "animate=harvesting 10000",
1265 "callobject=harvest",
1266 "animate=gathering 4000",
1267
1268=== modified file 'data/tribes/workers/barbarians/fisher/init.lua'
1269--- data/tribes/workers/barbarians/fisher/init.lua 2017-11-18 17:57:00 +0000
1270+++ data/tribes/workers/barbarians/fisher/init.lua 2019-04-23 16:25:43 +0000
1271@@ -33,10 +33,10 @@
1272 fish = {
1273 "findspace=size:any radius:7 resource:fish",
1274 "walk=coords",
1275- "playsound=sound/fisher fisher_throw_net 192",
1276+ "playsound=sound/fisher/fisher_throw_net 192",
1277 "mine=fish 1",
1278 "animate=fishing 3000",
1279- "playsound=sound/fisher fisher_pull_net 192",
1280+ "playsound=sound/fisher/fisher_pull_net 192",
1281 "createware=fish",
1282 "return"
1283 }
1284
1285=== modified file 'data/tribes/workers/barbarians/geologist/init.lua'
1286--- data/tribes/workers/barbarians/geologist/init.lua 2017-11-18 21:08:26 +0000
1287+++ data/tribes/workers/barbarians/geologist/init.lua 2019-04-23 16:25:43 +0000
1288@@ -41,7 +41,7 @@
1289 search = {
1290 "animate=hacking 5000",
1291 "animate=idle 2000",
1292- "playsound=sound/hammering geologist_hammer 192",
1293+ "playsound=sound/hammering/geologist_hammer 192",
1294 "animate=hacking 3000",
1295 "findresources"
1296 }
1297
1298=== modified file 'data/tribes/workers/barbarians/lumberjack/init.lua'
1299--- data/tribes/workers/barbarians/lumberjack/init.lua 2017-11-18 17:57:00 +0000
1300+++ data/tribes/workers/barbarians/lumberjack/init.lua 2019-04-23 16:25:43 +0000
1301@@ -34,10 +34,10 @@
1302 harvest = {
1303 "findobject=attrib:tree radius:10",
1304 "walk=object",
1305- "playsound=sound/woodcutting woodcutting 255",
1306+ "playsound=sound/woodcutting/woodcutting 255",
1307 "animate=hacking 10000",
1308 -- "playsound=sound/spoken timber 192",
1309- "playsound=sound/woodcutting tree-falling 130",
1310+ "playsound=sound/woodcutting/tree-falling 130",
1311 "callobject=fall",
1312 "animate=idle 2000",
1313 "createware=log",
1314
1315=== modified file 'data/tribes/workers/barbarians/shipwright/init.lua'
1316--- data/tribes/workers/barbarians/shipwright/init.lua 2018-02-28 09:38:13 +0000
1317+++ data/tribes/workers/barbarians/shipwright/init.lua 2019-04-23 16:25:43 +0000
1318@@ -8,8 +8,8 @@
1319 work = {
1320 pictures = path.list_files(dirname .. "work_??.png"),
1321 sound_effect = {
1322- directory = "sound/hammering",
1323- name = "hammering",
1324+ path = "sound/hammering/hammering",
1325+ priority = 64
1326 },
1327 hotspot = { 11, 26 },
1328 fps = 10
1329@@ -37,7 +37,7 @@
1330 buildship = {
1331 "walk=object-or-coords",
1332 "plant=attrib:shipconstruction unless object",
1333- "playsound=sound/sawmill sawmill 230",
1334+ "playsound=sound/sawmill/sawmill 230",
1335 "animate=work 500",
1336 "construct",
1337 "animate=work 5000",
1338
1339=== modified file 'data/tribes/workers/barbarians/stonemason/init.lua'
1340--- data/tribes/workers/barbarians/stonemason/init.lua 2017-11-18 17:57:00 +0000
1341+++ data/tribes/workers/barbarians/stonemason/init.lua 2019-04-23 16:25:43 +0000
1342@@ -34,7 +34,7 @@
1343 cut_granite = {
1344 "findobject=attrib:rocks radius:6",
1345 "walk=object",
1346- "playsound=sound/stonecutting stonecutter 192",
1347+ "playsound=sound/stonecutting/stonecutter 192",
1348 "animate=hacking 10000",
1349 "callobject=shrink",
1350 "createware=granite",
1351
1352=== modified file 'data/tribes/workers/empire/builder/init.lua'
1353--- data/tribes/workers/empire/builder/init.lua 2017-02-12 09:10:57 +0000
1354+++ data/tribes/workers/empire/builder/init.lua 2019-04-23 16:25:43 +0000
1355@@ -9,8 +9,8 @@
1356 work = {
1357 pictures = path.list_files(dirname .. "work_??.png"),
1358 sound_effect = {
1359- directory = "sound/hammering",
1360- name = "hammering",
1361+ path = "sound/hammering/hammering",
1362+ priority = 64
1363 },
1364 hotspot = { 11, 21 },
1365 fps = 10
1366
1367=== modified file 'data/tribes/workers/empire/farmer/init.lua'
1368--- data/tribes/workers/empire/farmer/init.lua 2018-02-28 09:38:13 +0000
1369+++ data/tribes/workers/empire/farmer/init.lua 2019-04-23 16:25:43 +0000
1370@@ -51,7 +51,7 @@
1371 harvest = {
1372 "findobject=attrib:ripe_wheat radius:2",
1373 "walk=object",
1374- "playsound=sound/farm scythe 220",
1375+ "playsound=sound/farm/scythe 220",
1376 "animate=harvesting 10000",
1377 "callobject=harvest",
1378 "animate=gathering 4000",
1379
1380=== modified file 'data/tribes/workers/empire/fisher/init.lua'
1381--- data/tribes/workers/empire/fisher/init.lua 2017-11-18 17:57:00 +0000
1382+++ data/tribes/workers/empire/fisher/init.lua 2019-04-23 16:25:43 +0000
1383@@ -33,10 +33,10 @@
1384 fish = {
1385 "findspace=size:any radius:7 resource:fish",
1386 "walk=coords",
1387- "playsound=sound/fisher fisher_throw_net 192",
1388+ "playsound=sound/fisher/fisher_throw_net 192",
1389 "mine=fish 1",
1390 "animate=fishing 3000", -- Play a fishing animation
1391- "playsound=sound/fisher fisher_pull_net 192",
1392+ "playsound=sound/fisher/fisher_pull_net 192",
1393 "createware=fish",
1394 "return"
1395 }
1396
1397=== modified file 'data/tribes/workers/empire/geologist/init.lua'
1398--- data/tribes/workers/empire/geologist/init.lua 2017-11-18 21:08:26 +0000
1399+++ data/tribes/workers/empire/geologist/init.lua 2019-04-23 16:25:43 +0000
1400@@ -41,7 +41,7 @@
1401 search = {
1402 "animate=hacking 5000",
1403 "animate=idle 2000",
1404- "playsound=sound/hammering geologist_hammer 192",
1405+ "playsound=sound/hammering/geologist_hammer 192",
1406 "animate=hacking 3000",
1407 "findresources"
1408 }
1409
1410=== modified file 'data/tribes/workers/empire/lumberjack/init.lua'
1411--- data/tribes/workers/empire/lumberjack/init.lua 2017-11-18 17:57:00 +0000
1412+++ data/tribes/workers/empire/lumberjack/init.lua 2019-04-23 16:25:43 +0000
1413@@ -34,10 +34,10 @@
1414 harvest = {
1415 "findobject=attrib:tree radius:10",
1416 "walk=object",
1417- "playsound=sound/woodcutting fast_woodcutting 250",
1418+ "playsound=sound/woodcutting/fast_woodcutting 250",
1419 "animate=hacking 10000",
1420- -- "playsound=sound/spoken timber 156",
1421- "playsound=sound/woodcutting tree-falling 130",
1422+ -- "playsound=sound/spoken/timber 156",
1423+ "playsound=sound/woodcutting/tree-falling 130",
1424 "callobject=fall",
1425 "animate=idle 2000",
1426 "createware=log",
1427
1428=== modified file 'data/tribes/workers/empire/shipwright/init.lua'
1429--- data/tribes/workers/empire/shipwright/init.lua 2018-02-28 09:38:13 +0000
1430+++ data/tribes/workers/empire/shipwright/init.lua 2019-04-23 16:25:43 +0000
1431@@ -8,8 +8,8 @@
1432 work = {
1433 pictures = path.list_files(dirname .. "work_??.png"),
1434 sound_effect = {
1435- directory = "sound/hammering",
1436- name = "hammering",
1437+ path = "sound/hammering/hammering",
1438+ priority = 64
1439 },
1440 hotspot = { 12, 27 },
1441 fps = 10
1442@@ -37,7 +37,7 @@
1443 buildship = {
1444 "walk=object-or-coords",
1445 "plant=attrib:shipconstruction unless object",
1446- "playsound=sound/sawmill sawmill 230",
1447+ "playsound=sound/sawmill/sawmill 230",
1448 "animate=work 500",
1449 "construct",
1450 "animate=work 5000",
1451
1452=== modified file 'data/tribes/workers/empire/stonemason/init.lua'
1453--- data/tribes/workers/empire/stonemason/init.lua 2017-11-18 17:57:00 +0000
1454+++ data/tribes/workers/empire/stonemason/init.lua 2019-04-23 16:25:43 +0000
1455@@ -34,7 +34,7 @@
1456 cut_granite = {
1457 "findobject=attrib:rocks radius:6",
1458 "walk=object",
1459- "playsound=sound/stonecutting stonecutter 220",
1460+ "playsound=sound/stonecutting/stonecutter 220",
1461 "animate=hacking 10000",
1462 "callobject=shrink",
1463 "createware=granite",
1464@@ -43,7 +43,7 @@
1465 cut_marble = {
1466 "findobject= attrib:rocks radius:6",
1467 "walk=object",
1468- "playsound=sound/stonecutting stonecutter 220",
1469+ "playsound=sound/stonecutting/stonecutter 220",
1470 "animate=hacking 10000",
1471 "callobject=shrink",
1472 "createware=marble",
1473
1474=== modified file 'data/tribes/workers/frisians/builder/init.lua'
1475--- data/tribes/workers/frisians/builder/init.lua 2018-11-30 10:27:21 +0000
1476+++ data/tribes/workers/frisians/builder/init.lua 2019-04-23 16:25:43 +0000
1477@@ -9,8 +9,8 @@
1478 work = {
1479 pictures = path.list_files (dirname .. "work_??.png"),
1480 sound_effect = {
1481- directory = "sound/hammering",
1482- name = "hammering",
1483+ path = "sound/hammering/hammering",
1484+ priority = 64
1485 },
1486 hotspot = {9, 24},
1487 fps = 10
1488
1489=== modified file 'data/tribes/workers/frisians/shipwright/init.lua'
1490--- data/tribes/workers/frisians/shipwright/init.lua 2018-11-30 10:27:21 +0000
1491+++ data/tribes/workers/frisians/shipwright/init.lua 2019-04-23 16:25:43 +0000
1492@@ -8,8 +8,8 @@
1493 work = {
1494 pictures = path.list_files (dirname .. "work_??.png"),
1495 sound_effect = {
1496- directory = "sound/hammering",
1497- name = "hammering",
1498+ path = "sound/hammering/hammering",
1499+ priority = 64
1500 },
1501 hotspot = {9, 24},
1502 fps = 10
1503
1504=== modified file 'data/world/critters/duck/init.lua'
1505--- data/world/critters/duck/init.lua 2017-02-12 09:10:57 +0000
1506+++ data/world/critters/duck/init.lua 2019-04-23 16:25:43 +0000
1507@@ -4,8 +4,7 @@
1508 idle = {
1509 pictures = path.list_files(dirname .. "idle_??.png"),
1510 sound_effect = {
1511- directory = dirname,
1512- name = "duck",
1513+ path = dirname .. "duck",
1514 },
1515 hotspot = { 5, 7 },
1516 fps = 4,
1517
1518=== modified file 'data/world/critters/elk/init.lua'
1519--- data/world/critters/elk/init.lua 2017-02-12 09:10:57 +0000
1520+++ data/world/critters/elk/init.lua 2019-04-23 16:25:43 +0000
1521@@ -7,8 +7,7 @@
1522 fps = 20,
1523 sound_effect = {
1524 -- Sound files with numbers starting for 10 are generating silence. Remove when we move the sound triggering to programs
1525- directory = "sound/animals",
1526- name = "elk",
1527+ path = "sound/animals/elk",
1528 },
1529 },
1530 }
1531
1532=== modified file 'data/world/critters/fox/init.lua'
1533--- data/world/critters/fox/init.lua 2017-02-12 09:10:57 +0000
1534+++ data/world/critters/fox/init.lua 2019-04-23 16:25:43 +0000
1535@@ -5,8 +5,7 @@
1536 pictures = path.list_files(dirname .. "idle_??.png"),
1537 sound_effect = {
1538 -- Sound files with numbers starting for 10 are generating silence. Remove when we move the sound triggering to programs
1539- directory = "sound/animals",
1540- name = "coyote",
1541+ path = "sound/animals/coyote",
1542 },
1543 hotspot = { 10, 13 },
1544 fps = 10,
1545
1546=== modified file 'data/world/critters/sheep/init.lua'
1547--- data/world/critters/sheep/init.lua 2017-02-12 09:10:57 +0000
1548+++ data/world/critters/sheep/init.lua 2019-04-23 16:25:43 +0000
1549@@ -4,8 +4,7 @@
1550 idle = {
1551 pictures = path.list_files(dirname .. "idle_??.png"),
1552 sound_effect = {
1553- directory = "sound/farm",
1554- name = "sheep",
1555+ path = "sound/farm/sheep",
1556 },
1557 hotspot = { 8, 16 },
1558 fps = 20,
1559
1560=== modified file 'data/world/critters/stag/init.lua'
1561--- data/world/critters/stag/init.lua 2017-02-12 09:10:57 +0000
1562+++ data/world/critters/stag/init.lua 2019-04-23 16:25:43 +0000
1563@@ -5,8 +5,7 @@
1564 pictures = path.list_files(dirname .. "idle_??.png"),
1565 sound_effect = {
1566 -- Sound files with numbers starting for 10 are generating silence. Remove when we move the sound triggering to programs
1567- directory = "sound/animals",
1568- name = "stag",
1569+ path = "sound/animals/stag",
1570 },
1571 hotspot = { 12, 26 },
1572 fps = 20,
1573
1574=== modified file 'data/world/critters/wildboar/init.lua'
1575--- data/world/critters/wildboar/init.lua 2017-02-12 09:10:57 +0000
1576+++ data/world/critters/wildboar/init.lua 2019-04-23 16:25:43 +0000
1577@@ -6,8 +6,7 @@
1578 hotspot = { 10, 18 },
1579 fps = 20,
1580 sound_effect = {
1581- directory = "sound/animals",
1582- name = "boar",
1583+ path = "sound/animals/boar",
1584 },
1585 },
1586 }
1587
1588=== modified file 'data/world/critters/wolf/init.lua'
1589--- data/world/critters/wolf/init.lua 2017-02-12 09:10:57 +0000
1590+++ data/world/critters/wolf/init.lua 2019-04-23 16:25:43 +0000
1591@@ -6,9 +6,8 @@
1592 hotspot = { 8, 15 },
1593 fps = 10,
1594 sound_effect = {
1595- -- Sound files with numbers starting for 10 are generating silence. Remove when we move the sound triggering to programs
1596- directory = "sound/animals",
1597- name = "wolf",
1598+ -- Sound files with numbers starting from 10 are generating silence.
1599+ path = "sound/animals/wolf",
1600 },
1601 },
1602 }
1603
1604=== modified file 'data/world/immovables/grass1/init.lua'
1605--- data/world/immovables/grass1/init.lua 2016-01-21 10:39:57 +0000
1606+++ data/world/immovables/grass1/init.lua 2019-04-23 16:25:43 +0000
1607@@ -12,8 +12,7 @@
1608 pictures = path.list_files(dirname .. "idle.png"),
1609 hotspot = { 10, 20 },
1610 sound_effect = {
1611- directory = "sound/animals",
1612- name = "frog1",
1613+ path = "sound/animals/frog1",
1614 },
1615 },
1616 }
1617
1618=== modified file 'data/world/immovables/grass2/init.lua'
1619--- data/world/immovables/grass2/init.lua 2016-01-21 10:39:57 +0000
1620+++ data/world/immovables/grass2/init.lua 2019-04-23 16:25:43 +0000
1621@@ -12,8 +12,7 @@
1622 pictures = path.list_files(dirname .. "idle.png"),
1623 hotspot = { 10, 16 },
1624 sound_effect = {
1625- directory = "sound/animals",
1626- name = "frog1",
1627+ path = "sound/animals/frog1",
1628 },
1629 },
1630 }
1631
1632=== modified file 'data/world/immovables/grass3/init.lua'
1633--- data/world/immovables/grass3/init.lua 2016-01-21 10:39:57 +0000
1634+++ data/world/immovables/grass3/init.lua 2019-04-23 16:25:43 +0000
1635@@ -12,8 +12,7 @@
1636 pictures = path.list_files(dirname .. "idle.png"),
1637 hotspot = { 10, 11 },
1638 sound_effect = {
1639- directory = "sound/animals",
1640- name = "frog1",
1641+ path = "sound/animals/frog1",
1642 },
1643 },
1644 }
1645
1646=== modified file 'data/world/immovables/trees/alder/init.lua'
1647--- data/world/immovables/trees/alder/init.lua 2018-11-03 11:27:18 +0000
1648+++ data/world/immovables/trees/alder/init.lua 2019-04-23 16:25:43 +0000
1649@@ -108,8 +108,7 @@
1650 hotspot = { 23, 59 },
1651 fps = 10,
1652 sound_effect = {
1653- directory = "sound/animals",
1654- name = "bird4",
1655+ path = "sound/animals/bird4",
1656 },
1657 },
1658 },
1659
1660=== modified file 'data/world/immovables/trees/aspen/init.lua'
1661--- data/world/immovables/trees/aspen/init.lua 2018-11-03 11:27:18 +0000
1662+++ data/world/immovables/trees/aspen/init.lua 2019-04-23 16:25:43 +0000
1663@@ -104,8 +104,7 @@
1664 hotspot = { 23, 58 },
1665 fps = 10,
1666 sound_effect = {
1667- directory = "sound/animals",
1668- name = "bird1",
1669+ path = "sound/animals/bird1",
1670 },
1671 },
1672 falling = {
1673
1674=== modified file 'data/world/immovables/trees/beech/init.lua'
1675--- data/world/immovables/trees/beech/init.lua 2018-11-03 11:27:18 +0000
1676+++ data/world/immovables/trees/beech/init.lua 2019-04-23 16:25:43 +0000
1677@@ -100,8 +100,7 @@
1678 hotspot = { 24, 60 },
1679 fps = 10,
1680 sound_effect = {
1681- directory = "sound/animals",
1682- name = "bird6",
1683+ path = "sound/animals/bird6",
1684 },
1685 },
1686 },
1687
1688=== modified file 'data/world/immovables/trees/birch/init.lua'
1689--- data/world/immovables/trees/birch/init.lua 2018-11-03 11:27:18 +0000
1690+++ data/world/immovables/trees/birch/init.lua 2019-04-23 16:25:43 +0000
1691@@ -103,8 +103,7 @@
1692 hotspot = { 23, 58 },
1693 fps = 10,
1694 sound_effect = {
1695- directory = "sound/animals",
1696- name = "bird5",
1697+ path = "sound/animals/bird5",
1698 },
1699 },
1700 },
1701
1702=== modified file 'data/world/immovables/trees/larch/init.lua'
1703--- data/world/immovables/trees/larch/init.lua 2018-11-03 11:27:18 +0000
1704+++ data/world/immovables/trees/larch/init.lua 2019-04-23 16:25:43 +0000
1705@@ -100,8 +100,7 @@
1706 hotspot = { 15, 59 },
1707 fps = 10,
1708 sound_effect = {
1709- directory = "sound/animals",
1710- name = "bird6",
1711+ path = "sound/animals/bird6",
1712 },
1713 },
1714 },
1715
1716=== modified file 'data/world/immovables/trees/maple/init.lua'
1717--- data/world/immovables/trees/maple/init.lua 2018-11-03 11:27:18 +0000
1718+++ data/world/immovables/trees/maple/init.lua 2019-04-23 16:25:43 +0000
1719@@ -100,8 +100,7 @@
1720 hotspot = { 23, 59 },
1721 fps = 10,
1722 sound_effect = {
1723- directory = "sound/animals",
1724- name = "bird4",
1725+ path = "sound/animals/bird4",
1726 },
1727 },
1728 },
1729
1730=== modified file 'data/world/immovables/trees/oak/init.lua'
1731--- data/world/immovables/trees/oak/init.lua 2018-11-03 11:27:18 +0000
1732+++ data/world/immovables/trees/oak/init.lua 2019-04-23 16:25:43 +0000
1733@@ -101,8 +101,7 @@
1734 hotspot = { 24, 60 },
1735 fps = 10,
1736 sound_effect = {
1737- directory = "sound/animals",
1738- name = "bird2",
1739+ path = "sound/animals/bird2",
1740 },
1741 },
1742 falling = {
1743
1744=== modified file 'data/world/immovables/trees/palm_borassus/init.lua'
1745--- data/world/immovables/trees/palm_borassus/init.lua 2018-11-03 11:27:18 +0000
1746+++ data/world/immovables/trees/palm_borassus/init.lua 2019-04-23 16:25:43 +0000
1747@@ -100,8 +100,7 @@
1748 hotspot = { 24, 60 },
1749 fps = 10,
1750 sound_effect = {
1751- directory = "sound/animals",
1752- name = "crickets1",
1753+ path = "sound/animals/crickets1",
1754 },
1755 },
1756 },
1757
1758=== modified file 'data/world/immovables/trees/palm_coconut/init.lua'
1759--- data/world/immovables/trees/palm_coconut/init.lua 2018-11-03 11:27:18 +0000
1760+++ data/world/immovables/trees/palm_coconut/init.lua 2019-04-23 16:25:43 +0000
1761@@ -100,8 +100,7 @@
1762 hotspot = { 24, 59 },
1763 fps = 10,
1764 sound_effect = {
1765- directory = "sound/animals",
1766- name = "bird3",
1767+ path = "sound/animals/bird3",
1768 },
1769 },
1770 },
1771
1772=== modified file 'data/world/immovables/trees/palm_date/init.lua'
1773--- data/world/immovables/trees/palm_date/init.lua 2018-11-03 11:27:18 +0000
1774+++ data/world/immovables/trees/palm_date/init.lua 2019-04-23 16:25:43 +0000
1775@@ -103,8 +103,7 @@
1776 hotspot = { 24, 60 },
1777 fps = 10,
1778 sound_effect = {
1779- directory = "sound/animals",
1780- name = "crickets1",
1781+ path = "sound/animals/crickets1",
1782 },
1783 },
1784 },
1785
1786=== modified file 'data/world/immovables/trees/palm_oil/init.lua'
1787--- data/world/immovables/trees/palm_oil/init.lua 2018-11-03 11:27:18 +0000
1788+++ data/world/immovables/trees/palm_oil/init.lua 2019-04-23 16:25:43 +0000
1789@@ -104,8 +104,7 @@
1790 hotspot = { 24, 60 },
1791 fps = 10,
1792 sound_effect = {
1793- directory = "sound/animals",
1794- name = "crickets2",
1795+ path = "sound/animals/crickets2",
1796 },
1797 },
1798 },
1799
1800=== modified file 'data/world/immovables/trees/palm_roystonea/init.lua'
1801--- data/world/immovables/trees/palm_roystonea/init.lua 2018-11-03 11:27:18 +0000
1802+++ data/world/immovables/trees/palm_roystonea/init.lua 2019-04-23 16:25:43 +0000
1803@@ -100,8 +100,7 @@
1804 hotspot = { 24, 60 },
1805 fps = 10,
1806 sound_effect = {
1807- directory = "sound/animals",
1808- name = "crickets2",
1809+ path = "sound/animals/crickets2",
1810 },
1811 },
1812 },
1813
1814=== modified file 'data/world/immovables/trees/rowan/init.lua'
1815--- data/world/immovables/trees/rowan/init.lua 2018-11-03 11:27:18 +0000
1816+++ data/world/immovables/trees/rowan/init.lua 2019-04-23 16:25:43 +0000
1817@@ -103,8 +103,7 @@
1818 hotspot = { 23, 59 },
1819 fps = 10,
1820 sound_effect = {
1821- directory = "sound/animals",
1822- name = "bird6",
1823+ path = "sound/animals/bird6",
1824 },
1825 },
1826 },
1827
1828=== modified file 'data/world/immovables/trees/spruce/init.lua'
1829--- data/world/immovables/trees/spruce/init.lua 2018-11-03 11:27:18 +0000
1830+++ data/world/immovables/trees/spruce/init.lua 2019-04-23 16:25:43 +0000
1831@@ -100,8 +100,7 @@
1832 hotspot = { 15, 59 },
1833 fps = 10,
1834 sound_effect = {
1835- directory = "sound/animals",
1836- name = "bird3",
1837+ path = "sound/animals/bird3",
1838 },
1839 },
1840 },
1841
1842=== modified file 'doc/sphinx/source/animations.rst'
1843--- doc/sphinx/source/animations.rst 2018-04-09 08:01:28 +0000
1844+++ doc/sphinx/source/animations.rst 2019-04-23 16:25:43 +0000
1845@@ -12,8 +12,8 @@
1846 scale = 2.5,
1847 fps = 4,
1848 sound_effect = {
1849- directory = "sound/foo",
1850- name = "bar",
1851+ path = "sound/foo/bar",
1852+ priority = 128
1853 },
1854 },
1855 working = ...
1856@@ -38,7 +38,11 @@
1857 specify the float value here. For example, if the animation images are 2.5 times the size of what should be blitted at default zoom, use ``scale = 2.5``.
1858
1859 **sound_effect**
1860- *Optional*. Our example will look for the sound files ``bar_00.ogg`` through ``bar_99.ogg`` in the directory ``data/sound/foo`` and play them in sequence.
1861+ *Optional*. Our example will look for the sound files ``bar_00.ogg`` through ``bar_99.ogg`` in the directory ``data/sound/foo`` and play them in sequence. The priority is optional with the default being ``1``, and its range is:
1862+
1863+ * **0-127:** Probability between ``0.0`` and ``1.0``, only one instance can be playing at any time
1864+ * **128-254:** Probability between ``0.0`` and ``1.0``, many instances can be playing at any time
1865+ * **255:** Always play
1866
1867
1868 Directional Animations
1869
1870=== modified file 'doc/sphinx/source/productionsite_program.rst'
1871--- doc/sphinx/source/productionsite_program.rst 2017-11-18 21:23:09 +0000
1872+++ doc/sphinx/source/productionsite_program.rst 2019-04-23 16:25:43 +0000
1873@@ -66,7 +66,7 @@
1874 "return=skipped unless economy needs snack",
1875 "sleep=5000",
1876 "consume=barbarians_bread fish,meat beer",
1877- "playsound=sound/barbarians/taverns inn 100",
1878+ "playsound=sound/barbarians/taverns/inn 100",
1879 "animate=working 22000",
1880 "sleep=10000",
1881 "produce=snack"
1882@@ -298,8 +298,8 @@
1883
1884 Parameter semantics:
1885
1886-``soundFX``
1887- The filename of a soundFX (relative to the productionsite's directory).
1888+``filepath``
1889+ The path/base_filename of a soundFX (relative to the data directory).
1890 ``priority``
1891 An integer. If omitted, 127 is used.
1892
1893
1894=== modified file 'regression_test.py'
1895--- regression_test.py 2017-09-15 13:23:09 +0000
1896+++ regression_test.py 2019-04-23 16:25:43 +0000
1897@@ -89,8 +89,7 @@
1898 '--datadir={}'.format(datadir()),
1899 '--datadir_for_testing={}'.format(datadir_for_testing()),
1900 '--homedir={}'.format(self.run_dir),
1901- '--disable_fx=true',
1902- '--disable_music=true',
1903+ '--nosound',
1904 '--language=en_US' ]
1905 args += [ "--{}={}".format(key, value) for key, value in iteritems(wlargs) ]
1906
1907
1908=== modified file 'src/economy/flag.h'
1909--- src/economy/flag.h 2019-02-27 17:19:00 +0000
1910+++ src/economy/flag.h 2019-04-23 16:25:43 +0000
1911@@ -156,6 +156,7 @@
1912 void draw(uint32_t gametime,
1913 TextToDraw draw_text,
1914 const Vector2f& point_on_dst,
1915+ const Coords& coords,
1916 float scale,
1917 RenderTarget* dst) override;
1918
1919
1920=== modified file 'src/economy/portdock.cc'
1921--- src/economy/portdock.cc 2019-02-27 17:19:00 +0000
1922+++ src/economy/portdock.cc 2019-04-23 16:25:43 +0000
1923@@ -140,7 +140,7 @@
1924 expedition_bootstrap_->set_economy(e);
1925 }
1926
1927-void PortDock::draw(uint32_t, const TextToDraw, const Vector2f&, float, RenderTarget*) {
1928+void PortDock::draw(uint32_t, const TextToDraw, const Vector2f&, const Widelands::Coords&, float, RenderTarget*) {
1929 // do nothing
1930 }
1931
1932
1933=== modified file 'src/economy/portdock.h'
1934--- src/economy/portdock.h 2019-02-27 17:19:00 +0000
1935+++ src/economy/portdock.h 2019-04-23 16:25:43 +0000
1936@@ -98,7 +98,7 @@
1937 PositionList get_positions(const EditorGameBase&) const override;
1938 void draw(uint32_t gametime,
1939 TextToDraw draw_text,
1940- const Vector2f& point_on_dst,
1941+ const Vector2f& point_on_dst, const Coords&,
1942 float scale,
1943 RenderTarget* dst) override;
1944
1945
1946=== modified file 'src/economy/road.h'
1947--- src/economy/road.h 2019-02-27 17:19:00 +0000
1948+++ src/economy/road.h 2019-04-23 16:25:43 +0000
1949@@ -131,8 +131,7 @@
1950 void cleanup(EditorGameBase&) override;
1951
1952 void draw(uint32_t gametime,
1953- TextToDraw draw_text,
1954- const Vector2f& point_on_dst,
1955+ TextToDraw draw_text, const Vector2f&, const Coords&,
1956 float scale,
1957 RenderTarget* dst) override;
1958
1959
1960=== modified file 'src/editor/editorinteractive.cc'
1961--- src/editor/editorinteractive.cc 2019-02-27 17:19:00 +0000
1962+++ src/editor/editorinteractive.cc 2019-04-23 16:25:43 +0000
1963@@ -300,14 +300,14 @@
1964 if (draw_immovables_) {
1965 Widelands::BaseImmovable* const imm = field.fcoords.field->get_immovable();
1966 if (imm != nullptr && imm->get_positions(ebase).front() == field.fcoords) {
1967- imm->draw(gametime, TextToDraw::kNone, field.rendertarget_pixel, scale, &dst);
1968+ imm->draw(gametime, TextToDraw::kNone, field.rendertarget_pixel, field.fcoords, scale, &dst);
1969 }
1970 }
1971
1972 if (draw_bobs_) {
1973 for (Widelands::Bob* bob = field.fcoords.field->get_first_bob(); bob;
1974 bob = bob->get_next_bob()) {
1975- bob->draw(ebase, TextToDraw::kNone, field.rendertarget_pixel, scale, &dst);
1976+ bob->draw(ebase, TextToDraw::kNone, field.rendertarget_pixel, field.fcoords, scale, &dst);
1977 }
1978 }
1979
1980@@ -405,6 +405,10 @@
1981 is_painting_ = false;
1982 }
1983
1984+bool EditorInteractive::player_hears_field(const Widelands::Coords&) const {
1985+ return true;
1986+}
1987+
1988 void EditorInteractive::on_buildhelp_changed(const bool value) {
1989 toggle_buildhelp_->set_perm_pressed(value);
1990 }
1991
1992=== modified file 'src/editor/editorinteractive.h'
1993--- src/editor/editorinteractive.h 2019-02-23 11:00:49 +0000
1994+++ src/editor/editorinteractive.h 2019-04-23 16:25:43 +0000
1995@@ -140,6 +140,7 @@
1996 private:
1997 friend struct EditorToolMenu;
1998
1999+ bool player_hears_field(const Widelands::Coords& coords) const override;
2000 void on_buildhelp_changed(const bool value) override;
2001
2002 void toggle_resources();
2003
2004=== modified file 'src/graphic/CMakeLists.txt'
2005--- src/graphic/CMakeLists.txt 2018-07-19 16:43:14 +0000
2006+++ src/graphic/CMakeLists.txt 2019-04-23 16:25:43 +0000
2007@@ -334,6 +334,8 @@
2008 io_filesystem
2009 io_stream
2010 logic_constants
2011+ logic_exceptions
2012+ logic_widelands_geometry
2013 note_sound
2014 notifications
2015 scripting_lua_interface
2016
2017=== modified file 'src/graphic/animation.cc'
2018--- src/graphic/animation.cc 2019-02-23 11:00:49 +0000
2019+++ src/graphic/animation.cc 2019-04-23 16:25:43 +0000
2020@@ -37,6 +37,7 @@
2021 #include "graphic/playercolor.h"
2022 #include "graphic/texture.h"
2023 #include "io/filesystem/layered_filesystem.h"
2024+#include "logic/game_data_error.h"
2025 #include "scripting/lua_table.h"
2026 #include "sound/note_sound.h"
2027 #include "sound/sound_handler.h"
2028@@ -62,14 +63,16 @@
2029 uint32_t frametime() const override;
2030 const Image* representative_image(const RGBColor* clr) const override;
2031 const std::string& representative_image_filename() const override;
2032- virtual void blit(uint32_t time,
2033+ virtual void blit(uint32_t time, const Widelands::Coords& coords,
2034 const Rectf& source_rect,
2035 const Rectf& destination_rect,
2036 const RGBColor* clr,
2037 Surface* target) const override;
2038- void trigger_sound(uint32_t framenumber, uint32_t stereo_position) const override;
2039-
2040 private:
2041+ // TODO(unknown): The chosen semantics of animation sound effects is problematic:
2042+ // What if the game runs very slowly or very quickly?
2043+ void trigger_sound(uint32_t framenumber, const Widelands::Coords& coords) const override;
2044+
2045 // Loads the graphics if they are not yet loaded.
2046 void ensure_graphics_are_loaded() const;
2047
2048@@ -88,10 +91,9 @@
2049 std::vector<const Image*> frames_;
2050 std::vector<const Image*> pcmasks_;
2051
2052- // name of sound effect that will be played at frame 0.
2053- // TODO(sirver): this should be done using playsound in a program instead of
2054- // binding it to the animation.
2055- std::string sound_effect_;
2056+ // ID of sound effect that will be played at frame 0.
2057+ FxId sound_effect_;
2058+ int32_t sound_priority_;
2059 bool play_once_;
2060 };
2061
2062@@ -100,15 +102,22 @@
2063 hotspot_(table.get_vector<std::string, int>("hotspot")),
2064 hasplrclrs_(false),
2065 scale_(1),
2066+ sound_effect_(kNoSoundEffect),
2067+ sound_priority_(kFxPriorityLowest),
2068 play_once_(false) {
2069 try {
2070 if (table.has_key("sound_effect")) {
2071 std::unique_ptr<LuaTable> sound_effects = table.get_table("sound_effect");
2072-
2073- const std::string name = sound_effects->get_string("name");
2074- const std::string directory = sound_effects->get_string("directory");
2075- sound_effect_ = directory + g_fs->file_separator() + name;
2076- g_sound_handler.load_fx_if_needed(directory, name, sound_effect_);
2077+ sound_effect_ = SoundHandler::register_fx(SoundType::kAmbient, sound_effects->get_string("path"));
2078+
2079+ if (sound_effects->has_key<std::string>("priority")) {
2080+ sound_priority_ = sound_effects->get_int("priority");
2081+ }
2082+
2083+ if (sound_priority_ < kFxPriorityLowest) {
2084+ throw Widelands::GameDataError("Minmum priority for sounds is %d, but only %d was specified for %s",
2085+ kFxPriorityLowest, sound_priority_, sound_effects->get_string("path").c_str());
2086+ }
2087 }
2088
2089 if (table.has_key("play_once")) {
2090@@ -236,15 +245,15 @@
2091 return 0;
2092 }
2093
2094-void NonPackedAnimation::trigger_sound(uint32_t time, uint32_t stereo_position) const {
2095- if (sound_effect_.empty()) {
2096+void NonPackedAnimation::trigger_sound(uint32_t time, const Widelands::Coords& coords) const {
2097+ if (sound_effect_ == kNoSoundEffect || coords == Widelands::Coords::null()) {
2098 return;
2099 }
2100
2101 const uint32_t framenumber = current_frame(time);
2102
2103 if (framenumber == 0) {
2104- Notifications::publish(NoteSound(sound_effect_, stereo_position, 1));
2105+ Notifications::publish(NoteSound(SoundType::kAmbient, sound_effect_, coords, sound_priority_));
2106 }
2107 }
2108
2109@@ -264,6 +273,7 @@
2110 }
2111
2112 void NonPackedAnimation::blit(uint32_t time,
2113+ const Widelands::Coords& coords,
2114 const Rectf& source_rect,
2115 const Rectf& destination_rect,
2116 const RGBColor* clr,
2117@@ -278,8 +288,7 @@
2118 target->blit_blended(
2119 destination_rect, *frames_.at(idx), *pcmasks_.at(idx), source_rect, *clr);
2120 }
2121- // TODO(GunChleoc): Stereo position would be nice.
2122- trigger_sound(time, 128);
2123+ trigger_sound(time, coords);
2124 }
2125
2126 } // namespace
2127
2128=== modified file 'src/graphic/animation.h'
2129--- src/graphic/animation.h 2019-02-23 11:00:49 +0000
2130+++ src/graphic/animation.h 2019-04-23 16:25:43 +0000
2131@@ -32,6 +32,7 @@
2132 #include "base/rect.h"
2133 #include "base/vector.h"
2134 #include "graphic/surface.h"
2135+#include "logic/widelands_geometry.h"
2136
2137 class Image;
2138 class LuaTable;
2139@@ -91,13 +92,18 @@
2140 /// in which case the neutral image will be blitted. The Surface is the 'target'
2141 /// for the blit operation and must be non-null.
2142 virtual void blit(uint32_t time,
2143+ const Widelands::Coords& coords,
2144 const Rectf& source_rect,
2145 const Rectf& destination_rect,
2146 const RGBColor* clr,
2147 Surface* target) const = 0;
2148
2149+protected:
2150 /// Play the sound effect associated with this animation at the given time.
2151- virtual void trigger_sound(uint32_t time, uint32_t stereo_position) const = 0;
2152+ /// Any sound effects are played with stereo position according to 'coords'.
2153+ /// If 'coords' == Widelands::Coords::null(), skip playing any sound effects.
2154+ virtual void trigger_sound(uint32_t time,
2155+ const Widelands::Coords& coords) const = 0;
2156
2157 private:
2158 DISALLOW_COPY_AND_ASSIGN(Animation);
2159
2160=== modified file 'src/graphic/game_renderer.cc'
2161--- src/graphic/game_renderer.cc 2019-03-13 06:55:41 +0000
2162+++ src/graphic/game_renderer.cc 2019-04-23 16:25:43 +0000
2163@@ -44,13 +44,13 @@
2164 uint32_t const anim_idx = field.owner->tribe().frontier_animation();
2165 if (field.vision) {
2166 dst->blit_animation(
2167- field.rendertarget_pixel, scale, anim_idx, 0, &field.owner->get_playercolor());
2168+ field.rendertarget_pixel, field.fcoords, scale, anim_idx, 0, &field.owner->get_playercolor());
2169 }
2170 for (const auto& nf : {fields_to_draw.at(field.rn_index), fields_to_draw.at(field.bln_index),
2171 fields_to_draw.at(field.brn_index)}) {
2172 if ((field.vision || nf.vision) && nf.is_border &&
2173 (field.owner == nf.owner || nf.owner == nullptr)) {
2174- dst->blit_animation(middle(field.rendertarget_pixel, nf.rendertarget_pixel), scale,
2175+ dst->blit_animation(middle(field.rendertarget_pixel, nf.rendertarget_pixel), Widelands::Coords::null(), scale,
2176 anim_idx, 0, &field.owner->get_playercolor());
2177 }
2178 }
2179
2180=== modified file 'src/graphic/rendertarget.cc'
2181--- src/graphic/rendertarget.cc 2019-04-23 15:10:01 +0000
2182+++ src/graphic/rendertarget.cc 2019-04-23 16:25:43 +0000
2183@@ -286,22 +286,20 @@
2184 }
2185
2186 void RenderTarget::blit_animation(const Vector2f& dst,
2187- const float scale,
2188- uint32_t animation_id,
2189- uint32_t time,
2190- const RGBColor* player_color,
2191- const int percent_from_bottom) {
2192+ const Widelands::Coords& coords,
2193+ const float scale,
2194+ uint32_t animation_id,
2195+ uint32_t time,
2196+ const RGBColor* player_color,
2197+ const int percent_from_bottom) {
2198 const Animation& animation = g_gr->animations().get_animation(animation_id);
2199- // TODO(unknown): Correctly calculate the stereo position for sound effects
2200- // TODO(unknown): The chosen semantics of animation sound effects is problematic:
2201- // What if the game runs very slowly or very quickly?
2202 assert(percent_from_bottom <= 100);
2203 if (percent_from_bottom > 0) {
2204 // Scaling for zoom and animation image size, then fit screen edges.
2205 Rectf srcrc = animation.source_rectangle(percent_from_bottom);
2206 Rectf dstrc = animation.destination_rectangle(dst, srcrc, scale);
2207 if (to_surface_geometry(&dstrc, &srcrc)) {
2208- animation.blit(time, srcrc, dstrc, player_color, surface_);
2209+ animation.blit(time, coords, srcrc, dstrc, player_color, surface_);
2210 }
2211 }
2212 }
2213
2214=== modified file 'src/graphic/rendertarget.h'
2215--- src/graphic/rendertarget.h 2019-04-23 15:10:01 +0000
2216+++ src/graphic/rendertarget.h 2019-04-23 16:25:43 +0000
2217@@ -27,6 +27,7 @@
2218 #include "graphic/blend_mode.h"
2219 #include "graphic/color.h"
2220 #include "graphic/image.h"
2221+#include "logic/widelands_geometry.h"
2222
2223 class Animation;
2224 class Surface;
2225@@ -105,12 +106,15 @@
2226 // Draw the 'animation' as it should appear at 'time' in this target at
2227 // 'dst'. Optionally, the animation is tinted with 'player_color' and
2228 // cropped to 'source_rect'.
2229+ // Any sound effects are played with stereo position according to 'coords'.
2230+ // If 'coords' == Widelands::Coords::null(), skip playing any sound effects.
2231 void blit_animation(const Vector2f& dst,
2232- const float scale,
2233- uint32_t animation_id,
2234- uint32_t time,
2235- const RGBColor* player_color = nullptr,
2236- const int percent_from_bottom = 100);
2237+ const Widelands::Coords& coords,
2238+ const float scale,
2239+ uint32_t animation_id,
2240+ uint32_t time,
2241+ const RGBColor* player_color = nullptr,
2242+ const int percent_from_bottom = 100);
2243
2244 void reset();
2245
2246
2247=== modified file 'src/logic/CMakeLists.txt'
2248--- src/logic/CMakeLists.txt 2019-02-27 17:19:00 +0000
2249+++ src/logic/CMakeLists.txt 2019-04-23 16:25:43 +0000
2250@@ -310,6 +310,7 @@
2251 scripting_lua_interface
2252 scripting_lua_table
2253 sound
2254+ sound_constants
2255 ui_basic
2256 wui
2257 wui_mapview_pixelfunctions
2258
2259=== modified file 'src/logic/editor_game_base.cc'
2260--- src/logic/editor_game_base.cc 2019-03-19 21:33:01 +0000
2261+++ src/logic/editor_game_base.cc 2019-04-23 16:25:43 +0000
2262@@ -53,6 +53,7 @@
2263 #include "map_io/map_saver.h"
2264 #include "scripting/logic.h"
2265 #include "scripting/lua_table.h"
2266+#include "sound/sound_handler.h"
2267 #include "ui_basic/progresswindow.h"
2268 #include "wui/interactive_base.h"
2269 #include "wui/interactive_gamebase.h"
2270@@ -78,6 +79,9 @@
2271
2272 EditorGameBase::~EditorGameBase() {
2273 delete_tempfile();
2274+ if (g_sh != nullptr) {
2275+ g_sh->remove_fx_set(SoundType::kAmbient);
2276+ }
2277 }
2278
2279 /**
2280
2281=== modified file 'src/logic/game.cc'
2282--- src/logic/game.cc 2019-03-24 11:58:54 +0000
2283+++ src/logic/game.cc 2019-04-23 16:25:43 +0000
2284@@ -64,6 +64,7 @@
2285 #include "network/network.h"
2286 #include "scripting/logic.h"
2287 #include "scripting/lua_table.h"
2288+#include "sound/sound_handler.h"
2289 #include "ui_basic/progresswindow.h"
2290 #include "wui/game_tips.h"
2291 #include "wui/interactive_player.h"
2292@@ -548,7 +549,7 @@
2293 ;
2294 #endif
2295
2296- g_sound_handler.change_music("ingame", 1000, 0);
2297+ g_sh->change_music("ingame", 1000);
2298
2299 state_ = gs_running;
2300
2301@@ -556,7 +557,7 @@
2302
2303 state_ = gs_ending;
2304
2305- g_sound_handler.change_music("menu", 1000, 0);
2306+ g_sh->change_music("menu", 1000);
2307
2308 cleanup_objects();
2309 set_ibase(nullptr);
2310
2311=== modified file 'src/logic/map_objects/bob.cc'
2312--- src/logic/map_objects/bob.cc 2019-04-23 15:10:01 +0000
2313+++ src/logic/map_objects/bob.cc 2019-04-23 16:25:43 +0000
2314@@ -759,6 +759,7 @@
2315 void Bob::draw(const EditorGameBase& egbase,
2316 const TextToDraw&,
2317 const Vector2f& field_on_dst,
2318+ const Widelands::Coords& coords,
2319 const float scale,
2320 RenderTarget* dst) const {
2321 if (!anim_) {
2322@@ -767,8 +768,8 @@
2323
2324 auto* const bob_owner = get_owner();
2325 const Vector2f point_on_dst = calc_drawpos(egbase, field_on_dst, scale);
2326- dst->blit_animation(point_on_dst, scale, anim_, egbase.get_gametime() - animstart_,
2327- (bob_owner == nullptr) ? nullptr : &bob_owner->get_playercolor());
2328+ dst->blit_animation(point_on_dst, coords, scale, anim_, egbase.get_gametime() - animstart_,
2329+ (bob_owner == nullptr) ? nullptr : &bob_owner->get_playercolor());
2330 }
2331
2332 /**
2333
2334=== modified file 'src/logic/map_objects/bob.h'
2335--- src/logic/map_objects/bob.h 2019-02-27 17:19:00 +0000
2336+++ src/logic/map_objects/bob.h 2019-04-23 16:25:43 +0000
2337@@ -265,7 +265,7 @@
2338 // required to draw the bob in the right size.
2339 virtual void draw(const EditorGameBase&,
2340 const TextToDraw& draw_text,
2341- const Vector2f& field_on_dst,
2342+ const Vector2f& field_on_dst, const Coords& coords,
2343 float scale,
2344 RenderTarget* dst) const;
2345
2346
2347=== modified file 'src/logic/map_objects/immovable.cc'
2348--- src/logic/map_objects/immovable.cc 2019-04-23 15:10:01 +0000
2349+++ src/logic/map_objects/immovable.cc 2019-04-23 16:25:43 +0000
2350@@ -456,24 +456,26 @@
2351 void Immovable::draw(uint32_t gametime,
2352 const TextToDraw draw_text,
2353 const Vector2f& point_on_dst,
2354+ const Widelands::Coords& coords,
2355 float scale,
2356 RenderTarget* dst) {
2357 if (!anim_) {
2358 return;
2359 }
2360 if (!anim_construction_total_) {
2361- dst->blit_animation(point_on_dst, scale, anim_, gametime - animstart_);
2362+ dst->blit_animation(point_on_dst, coords, scale, anim_, gametime - animstart_);
2363 if (former_building_descr_) {
2364 do_draw_info(draw_text, former_building_descr_->descname(), "", point_on_dst, scale, dst);
2365 }
2366 } else {
2367- draw_construction(gametime, draw_text, point_on_dst, scale, dst);
2368+ draw_construction(gametime, draw_text, point_on_dst, coords, scale, dst);
2369 }
2370 }
2371
2372 void Immovable::draw_construction(const uint32_t gametime,
2373 const TextToDraw draw_text,
2374 const Vector2f& point_on_dst,
2375+ const Widelands::Coords& coords,
2376 const float scale,
2377 RenderTarget* dst) {
2378 const ImmovableProgram::ActConstruct* constructionact = nullptr;
2379@@ -504,13 +506,13 @@
2380 if (current_frame > 0) {
2381 // Not the first pic, so draw the previous one in the back
2382 dst->blit_animation(
2383- point_on_dst, scale, anim_, (current_frame - 1) * frametime, &player_color);
2384+ point_on_dst, Widelands::Coords::null(), scale, anim_, (current_frame - 1) * frametime, &player_color);
2385 }
2386
2387 const int percent = ((done % units_per_frame) * 100) / units_per_frame;
2388
2389 dst->blit_animation(
2390- point_on_dst, scale, anim_, current_frame * frametime, &player_color, percent);
2391+ point_on_dst, coords, scale, anim_, current_frame * frametime, &player_color, percent);
2392
2393 // Additionally, if statistics are enabled, draw a progression string
2394 do_draw_info(draw_text, descr().descname(),
2395@@ -791,7 +793,7 @@
2396 ImmovableProgram::ActPlaySound::ActPlaySound(char* parameters, const ImmovableDescr&) {
2397 try {
2398 bool reached_end;
2399- name = next_word(parameters, reached_end);
2400+ std::string name = next_word(parameters, reached_end);
2401
2402 if (!reached_end) {
2403 char* endp;
2404@@ -802,8 +804,7 @@
2405 } else
2406 priority = 127;
2407
2408- g_sound_handler.load_fx_if_needed(
2409- FileSystem::fs_dirname(name), FileSystem::fs_filename(name.c_str()), name);
2410+ fx = g_sh->register_fx(SoundType::kAmbient, name);
2411 } catch (const WException& e) {
2412 throw GameDataError("playsound: %s", e.what());
2413 }
2414@@ -813,7 +814,7 @@
2415 * Whether the effect actually gets played
2416 * is decided only by the sound server*/
2417 void ImmovableProgram::ActPlaySound::execute(Game& game, Immovable& immovable) const {
2418- Notifications::publish(NoteSound(name, immovable.get_position(), priority));
2419+ Notifications::publish(NoteSound(SoundType::kAmbient, fx, immovable.get_position(), priority));
2420 immovable.program_step(game);
2421 }
2422
2423
2424=== modified file 'src/logic/map_objects/immovable.h'
2425--- src/logic/map_objects/immovable.h 2019-02-27 17:19:00 +0000
2426+++ src/logic/map_objects/immovable.h 2019-04-23 16:25:43 +0000
2427@@ -105,6 +105,7 @@
2428 virtual void draw(uint32_t gametime,
2429 TextToDraw draw_text,
2430 const Vector2f& point_on_dst,
2431+ const Coords& coords,
2432 float scale,
2433 RenderTarget* dst) = 0;
2434
2435@@ -229,7 +230,7 @@
2436 void act(Game&, uint32_t data) override;
2437 void draw(uint32_t gametime,
2438 TextToDraw draw_text,
2439- const Vector2f& point_on_dst,
2440+ const Vector2f& point_on_dst, const Coords& coords,
2441 float scale,
2442 RenderTarget* dst) override;
2443
2444@@ -316,6 +317,7 @@
2445 void draw_construction(uint32_t gametime,
2446 TextToDraw draw_text,
2447 const Vector2f& point_on_dst,
2448+ const Widelands::Coords& coords,
2449 float scale,
2450 RenderTarget* dst);
2451 };
2452
2453=== modified file 'src/logic/map_objects/immovable_program.h'
2454--- src/logic/map_objects/immovable_program.h 2019-02-23 11:00:49 +0000
2455+++ src/logic/map_objects/immovable_program.h 2019-04-23 16:25:43 +0000
2456@@ -133,10 +133,9 @@
2457 /// Parameter syntax:
2458 /// parameters ::= directory sound [priority]
2459 /// Parameter semantics:
2460- /// directory:
2461- /// The directory of the sound files, relative to the datadir.
2462- /// sound:
2463- /// The base filename of a sound effect (relative to the directory).
2464+ /// path:
2465+ /// The directory of the sound files, relative to the datadir, followed
2466+ /// by the base filename of a sound effect (relative to the directory).
2467 /// priority:
2468 /// An integer. If omitted, 127 is used.
2469 ///
2470@@ -147,7 +146,7 @@
2471 void execute(Game&, Immovable&) const override;
2472
2473 private:
2474- std::string name;
2475+ FxId fx;
2476 uint8_t priority;
2477 };
2478
2479
2480=== modified file 'src/logic/map_objects/tribes/building.cc'
2481--- src/logic/map_objects/tribes/building.cc 2019-04-23 15:10:01 +0000
2482+++ src/logic/map_objects/tribes/building.cc 2019-04-23 16:25:43 +0000
2483@@ -608,10 +608,11 @@
2484 void Building::draw(uint32_t gametime,
2485 const TextToDraw draw_text,
2486 const Vector2f& point_on_dst,
2487+ const Widelands::Coords& coords,
2488 const float scale,
2489 RenderTarget* dst) {
2490 dst->blit_animation(
2491- point_on_dst, scale, anim_, gametime - animstart_, &get_owner()->get_playercolor());
2492+ point_on_dst, coords, scale, anim_, gametime - animstart_, &get_owner()->get_playercolor());
2493
2494 // door animation?
2495
2496
2497=== modified file 'src/logic/map_objects/tribes/building.h'
2498--- src/logic/map_objects/tribes/building.h 2019-02-27 17:19:00 +0000
2499+++ src/logic/map_objects/tribes/building.h 2019-04-23 16:25:43 +0000
2500@@ -341,7 +341,7 @@
2501
2502 void draw(uint32_t gametime,
2503 TextToDraw draw_text,
2504- const Vector2f& point_on_dst,
2505+ const Vector2f& point_on_dst, const Coords& coords,
2506 float scale,
2507 RenderTarget* dst) override;
2508 void
2509
2510=== modified file 'src/logic/map_objects/tribes/constructionsite.cc'
2511--- src/logic/map_objects/tribes/constructionsite.cc 2019-03-13 06:55:41 +0000
2512+++ src/logic/map_objects/tribes/constructionsite.cc 2019-04-23 16:25:43 +0000
2513@@ -35,11 +35,14 @@
2514 #include "logic/game.h"
2515 #include "logic/map_objects/tribes/tribe_descr.h"
2516 #include "logic/map_objects/tribes/worker.h"
2517+#include "sound/note_sound.h"
2518+#include "sound/sound_handler.h"
2519 #include "ui_basic/window.h"
2520
2521 namespace Widelands {
2522
2523 void ConstructionsiteInformation::draw(const Vector2f& point_on_dst,
2524+ const Widelands::Coords& coords,
2525 float scale,
2526 const RGBColor& player_color,
2527 RenderTarget* dst) const {
2528@@ -55,11 +58,11 @@
2529
2530 if (cur_frame) { // not the first pic
2531 // Draw the complete prev pic , so we won't run into trouble if images have different sizes
2532- dst->blit_animation(point_on_dst, scale, anim_idx, anim_time - FRAME_LENGTH, &player_color);
2533+ dst->blit_animation(point_on_dst, Widelands::Coords::null(), scale, anim_idx, anim_time - FRAME_LENGTH, &player_color);
2534 } else if (was) {
2535 // Is the first picture but there was another building here before,
2536 // get its most fitting picture and draw it instead.
2537- dst->blit_animation(point_on_dst, scale, was->get_unoccupied_animation(),
2538+ dst->blit_animation(point_on_dst, Widelands::Coords::null(), scale, was->get_unoccupied_animation(),
2539 anim_time - FRAME_LENGTH, &player_color);
2540 }
2541 // Now blit a segment of the current construction phase from the bottom.
2542@@ -68,7 +71,7 @@
2543 percent /= totaltime;
2544 }
2545 percent -= 100 * cur_frame;
2546- dst->blit_animation(point_on_dst, scale, anim_idx, anim_time, &player_color, percent);
2547+ dst->blit_animation(point_on_dst, coords, scale, anim_idx, anim_time, &player_color, percent);
2548 }
2549
2550 /**
2551@@ -78,7 +81,8 @@
2552 ConstructionSiteDescr::ConstructionSiteDescr(const std::string& init_descname,
2553 const LuaTable& table,
2554 const EditorGameBase& egbase)
2555- : BuildingDescr(init_descname, MapObjectType::CONSTRUCTIONSITE, table, egbase) {
2556+ : BuildingDescr(init_descname, MapObjectType::CONSTRUCTIONSITE, table, egbase),
2557+ creation_fx_(SoundHandler::register_fx(SoundType::kAmbient, "sound/create_construction_site")) {
2558 add_attribute(MapObject::CONSTRUCTIONSITE);
2559 }
2560
2561@@ -86,6 +90,10 @@
2562 return *new ConstructionSite(*this);
2563 }
2564
2565+FxId ConstructionSiteDescr::creation_fx() const {
2566+ return creation_fx_;
2567+}
2568+
2569 /*
2570 ==============================
2571
2572@@ -143,6 +151,7 @@
2573 ===============
2574 */
2575 bool ConstructionSite::init(EditorGameBase& egbase) {
2576+ Notifications::publish(NoteSound(SoundType::kAmbient, descr().creation_fx(), position_, kFxPriorityAlwaysPlay));
2577 PartiallyFinishedBuilding::init(egbase);
2578
2579 const std::map<DescriptionIndex, uint8_t>* buildcost;
2580@@ -339,12 +348,13 @@
2581 void ConstructionSite::draw(uint32_t gametime,
2582 TextToDraw draw_text,
2583 const Vector2f& point_on_dst,
2584+ const Widelands::Coords& coords,
2585 float scale,
2586 RenderTarget* dst) {
2587 uint32_t tanim = gametime - animstart_;
2588 // Draw the construction site marker
2589 const RGBColor& player_color = get_owner()->get_playercolor();
2590- dst->blit_animation(point_on_dst, scale, anim_, tanim, &player_color);
2591+ dst->blit_animation(point_on_dst, Widelands::Coords::null(), scale, anim_, tanim, &player_color);
2592
2593 // Draw the partially finished building
2594
2595@@ -358,7 +368,7 @@
2596 info_.completedtime += CONSTRUCTIONSITE_STEP_TIME + gametime - work_steptime_;
2597 }
2598
2599- info_.draw(point_on_dst, scale, player_color, dst);
2600+ info_.draw(point_on_dst, coords, scale, player_color, dst);
2601
2602 // Draw help strings
2603 draw_info(draw_text, point_on_dst, scale, dst);
2604
2605=== modified file 'src/logic/map_objects/tribes/constructionsite.h'
2606--- src/logic/map_objects/tribes/constructionsite.h 2019-02-27 17:19:00 +0000
2607+++ src/logic/map_objects/tribes/constructionsite.h 2019-04-23 16:25:43 +0000
2608@@ -38,7 +38,7 @@
2609 }
2610
2611 /// Draw the partly finished constructionsite
2612- void draw(const Vector2f& point_on_dst,
2613+ void draw(const Vector2f& point_on_dst, const Coords& coords,
2614 float scale,
2615 const RGBColor& player_color,
2616 RenderTarget* dst) const;
2617@@ -78,7 +78,11 @@
2618
2619 Building& create_object() const override;
2620
2621+ FxId creation_fx() const;
2622+
2623 private:
2624+ const FxId creation_fx_;
2625+
2626 DISALLOW_COPY_AND_ASSIGN(ConstructionSiteDescr);
2627 };
2628
2629@@ -122,7 +126,7 @@
2630
2631 void draw(uint32_t gametime,
2632 TextToDraw draw_text,
2633- const Vector2f& point_on_dst,
2634+ const Vector2f& point_on_dst, const Coords& coords,
2635 float scale,
2636 RenderTarget* dst) override;
2637
2638
2639=== modified file 'src/logic/map_objects/tribes/dismantlesite.cc'
2640--- src/logic/map_objects/tribes/dismantlesite.cc 2019-03-13 06:55:41 +0000
2641+++ src/logic/map_objects/tribes/dismantlesite.cc 2019-04-23 16:25:43 +0000
2642@@ -34,6 +34,8 @@
2643 #include "logic/game.h"
2644 #include "logic/map_objects/tribes/tribe_descr.h"
2645 #include "logic/map_objects/tribes/worker.h"
2646+#include "sound/note_sound.h"
2647+#include "sound/sound_handler.h"
2648
2649 namespace Widelands {
2650
2651@@ -45,7 +47,8 @@
2652 DismantleSiteDescr::DismantleSiteDescr(const std::string& init_descname,
2653 const LuaTable& table,
2654 const EditorGameBase& egbase)
2655- : BuildingDescr(init_descname, MapObjectType::DISMANTLESITE, table, egbase) {
2656+ : BuildingDescr(init_descname, MapObjectType::DISMANTLESITE, table, egbase),
2657+ creation_fx_(SoundHandler::register_fx(SoundType::kAmbient, "sound/create_construction_site")) {
2658 add_attribute(MapObject::Attribute::CONSTRUCTIONSITE); // Yep, this is correct.
2659 }
2660
2661@@ -53,6 +56,10 @@
2662 return *new DismantleSite(*this);
2663 }
2664
2665+FxId DismantleSiteDescr::creation_fx() const {
2666+ return creation_fx_;
2667+}
2668+
2669 /*
2670 ==============================
2671
2672@@ -106,6 +113,8 @@
2673 ===============
2674 */
2675 bool DismantleSite::init(EditorGameBase& egbase) {
2676+ Notifications::publish(NoteSound(SoundType::kAmbient, descr().creation_fx(), position_, kFxPriorityAlwaysPlay));
2677+
2678 PartiallyFinishedBuilding::init(egbase);
2679
2680 for (const auto& ware : count_returned_wares(this)) {
2681@@ -219,16 +228,17 @@
2682 void DismantleSite::draw(uint32_t gametime,
2683 const TextToDraw draw_text,
2684 const Vector2f& point_on_dst,
2685+ const Widelands::Coords& coords,
2686 float scale,
2687 RenderTarget* dst) {
2688 uint32_t tanim = gametime - animstart_;
2689 const RGBColor& player_color = get_owner()->get_playercolor();
2690
2691 // Draw the construction site marker
2692- dst->blit_animation(point_on_dst, scale, anim_, tanim, &player_color);
2693+ dst->blit_animation(point_on_dst, Widelands::Coords::null(), scale, anim_, tanim, &player_color);
2694
2695 // Blit bottom part of the animation according to dismantle progress
2696- dst->blit_animation(point_on_dst, scale, building_->get_unoccupied_animation(), tanim,
2697+ dst->blit_animation(point_on_dst, coords, scale, building_->get_unoccupied_animation(), tanim,
2698 &player_color, 100 - ((get_built_per64k() * 100) >> 16));
2699
2700 // Draw help strings
2701
2702=== modified file 'src/logic/map_objects/tribes/dismantlesite.h'
2703--- src/logic/map_objects/tribes/dismantlesite.h 2019-02-27 17:19:00 +0000
2704+++ src/logic/map_objects/tribes/dismantlesite.h 2019-04-23 16:25:43 +0000
2705@@ -53,7 +53,11 @@
2706
2707 Building& create_object() const override;
2708
2709+ FxId creation_fx() const;
2710+
2711 private:
2712+ const FxId creation_fx_;
2713+
2714 DISALLOW_COPY_AND_ASSIGN(DismantleSiteDescr);
2715 };
2716
2717@@ -90,6 +94,7 @@
2718 void draw(uint32_t gametime,
2719 TextToDraw draw_text,
2720 const Vector2f& point_on_dst,
2721+ const Widelands::Coords& coords,
2722 float scale,
2723 RenderTarget* dst) override;
2724 };
2725
2726=== modified file 'src/logic/map_objects/tribes/partially_finished_building.cc'
2727--- src/logic/map_objects/tribes/partially_finished_building.cc 2019-02-23 11:00:49 +0000
2728+++ src/logic/map_objects/tribes/partially_finished_building.cc 2019-04-23 16:25:43 +0000
2729@@ -26,7 +26,6 @@
2730 #include "logic/map_objects/tribes/tribe_descr.h"
2731 #include "logic/map_objects/tribes/worker.h"
2732 #include "logic/player.h"
2733-#include "sound/note_sound.h"
2734
2735 namespace Widelands {
2736
2737@@ -69,10 +68,10 @@
2738 bool PartiallyFinishedBuilding::init(EditorGameBase& egbase) {
2739 Building::init(egbase);
2740
2741- if (upcast(Game, game, &egbase))
2742+ if (upcast(Game, game, &egbase)) {
2743 request_builder(*game);
2744+ }
2745
2746- Notifications::publish(NoteSound("create_construction_site", position_, 255));
2747 return true;
2748 }
2749
2750
2751=== modified file 'src/logic/map_objects/tribes/production_program.cc'
2752--- src/logic/map_objects/tribes/production_program.cc 2019-04-11 05:45:55 +0000
2753+++ src/logic/map_objects/tribes/production_program.cc 2019-04-23 16:25:43 +0000
2754@@ -1425,9 +1425,8 @@
2755 ProductionProgram::ActPlaySound::ActPlaySound(char* parameters) {
2756 try {
2757 bool reached_end;
2758- const std::string& filepath = next_word(parameters, reached_end);
2759- const std::string& filename = next_word(parameters, reached_end);
2760- name = filepath + g_fs->file_separator() + filename;
2761+ const char* const name = next_word(parameters, reached_end);
2762+ fx = SoundHandler::register_fx(SoundType::kAmbient, name);
2763
2764 if (!reached_end) {
2765 char* endp;
2766@@ -1435,17 +1434,20 @@
2767 priority = value;
2768 if (*endp || priority != value)
2769 throw GameDataError("expected %s but found \"%s\"", "priority", parameters);
2770- } else
2771- priority = 127;
2772-
2773- g_sound_handler.load_fx_if_needed(filepath, filename, name);
2774+ } else {
2775+ priority = kFxPriorityAllowMultiple - 1;
2776+ }
2777+ if (priority < kFxPriorityLowest) {
2778+ throw GameDataError("Minmum priority for sounds is %d, but only %d was specified for %s",
2779+ kFxPriorityLowest, priority, name);
2780+ }
2781 } catch (const WException& e) {
2782 throw GameDataError("playsound: %s", e.what());
2783 }
2784 }
2785
2786 void ProductionProgram::ActPlaySound::execute(Game& game, ProductionSite& ps) const {
2787- Notifications::publish(NoteSound(name, ps.position_, priority));
2788+ Notifications::publish(NoteSound(SoundType::kAmbient, fx, ps.position_, priority));
2789 return ps.program_step(game);
2790 }
2791
2792
2793=== modified file 'src/logic/map_objects/tribes/production_program.h'
2794--- src/logic/map_objects/tribes/production_program.h 2019-02-23 11:00:49 +0000
2795+++ src/logic/map_objects/tribes/production_program.h 2019-04-23 16:25:43 +0000
2796@@ -503,7 +503,7 @@
2797 void execute(Game&, ProductionSite&) const override;
2798
2799 private:
2800- std::string name;
2801+ FxId fx;
2802 uint8_t priority;
2803 };
2804
2805
2806=== modified file 'src/logic/map_objects/tribes/ship.cc'
2807--- src/logic/map_objects/tribes/ship.cc 2019-02-27 17:19:00 +0000
2808+++ src/logic/map_objects/tribes/ship.cc 2019-04-23 16:25:43 +0000
2809@@ -979,10 +979,11 @@
2810
2811 void Ship::draw(const EditorGameBase& egbase,
2812 const TextToDraw& draw_text,
2813- const Vector2f& field_on_dst,
2814+ const Vector2f& point_on_dst,
2815+ const Widelands::Coords& coords,
2816 const float scale,
2817 RenderTarget* dst) const {
2818- Bob::draw(egbase, draw_text, field_on_dst, scale, dst);
2819+ Bob::draw(egbase, draw_text, point_on_dst, coords, scale, dst);
2820
2821 // Show ship name and current activity
2822 std::string statistics_string;
2823@@ -1024,7 +1025,7 @@
2824 .str();
2825 }
2826
2827- do_draw_info(draw_text, shipname_, statistics_string, calc_drawpos(egbase, field_on_dst, scale),
2828+ do_draw_info(draw_text, shipname_, statistics_string, calc_drawpos(egbase, point_on_dst, scale),
2829 scale, dst);
2830 }
2831
2832
2833=== modified file 'src/logic/map_objects/tribes/ship.h'
2834--- src/logic/map_objects/tribes/ship.h 2019-02-27 17:19:00 +0000
2835+++ src/logic/map_objects/tribes/ship.h 2019-04-23 16:25:43 +0000
2836@@ -234,7 +234,7 @@
2837 protected:
2838 void draw(const EditorGameBase&,
2839 const TextToDraw& draw_text,
2840- const Vector2f& field_on_dst,
2841+ const Vector2f& point_on_dst, const Coords& coords,
2842 float scale,
2843 RenderTarget* dst) const override;
2844
2845
2846=== modified file 'src/logic/map_objects/tribes/soldier.cc'
2847--- src/logic/map_objects/tribes/soldier.cc 2019-02-28 12:22:36 +0000
2848+++ src/logic/map_objects/tribes/soldier.cc 2019-04-23 16:25:43 +0000
2849@@ -440,8 +440,8 @@
2850 */
2851 void Soldier::draw(const EditorGameBase& game,
2852 const TextToDraw&,
2853- const Vector2f& field_on_dst,
2854- const float scale,
2855+ const Vector2f& field_on_dst, const Coords& coords,
2856+ float scale,
2857 RenderTarget* dst) const {
2858 const uint32_t anim = get_current_anim();
2859 if (!anim) {
2860@@ -453,7 +453,7 @@
2861 point_on_dst.cast<int>() -
2862 Vector2i(0, (g_gr->animations().get_animation(get_current_anim()).height() - 7) * scale),
2863 scale, true, dst);
2864- draw_inner(game, point_on_dst, scale, dst);
2865+ draw_inner(game, point_on_dst, coords, scale, dst);
2866 }
2867
2868 /**
2869
2870=== modified file 'src/logic/map_objects/tribes/soldier.h'
2871--- src/logic/map_objects/tribes/soldier.h 2019-02-27 17:19:00 +0000
2872+++ src/logic/map_objects/tribes/soldier.h 2019-04-23 16:25:43 +0000
2873@@ -211,6 +211,7 @@
2874 void draw(const EditorGameBase&,
2875 const TextToDraw& draw_text,
2876 const Vector2f& point_on_dst,
2877+ const Widelands::Coords& coords,
2878 float scale,
2879 RenderTarget* dst) const override;
2880
2881
2882=== modified file 'src/logic/map_objects/tribes/worker.cc'
2883--- src/logic/map_objects/tribes/worker.cc 2019-04-23 15:10:01 +0000
2884+++ src/logic/map_objects/tribes/worker.cc 2019-04-23 16:25:43 +0000
2885@@ -995,11 +995,11 @@
2886 }
2887
2888 /**
2889- * Demand from the g_sound_handler to play a certain sound effect.
2890+ * Demand from the g_sh to play a certain sound effect.
2891 * Whether the effect actually gets played is decided only by the sound server.
2892 */
2893 bool Worker::run_playsound(Game& game, State& state, const Action& action) {
2894- Notifications::publish(NoteSound(action.sparam1, get_position(), action.iparam1));
2895+ Notifications::publish(NoteSound(SoundType::kAmbient, action.iparam2, get_position(), action.iparam1));
2896
2897 ++state.ivar1;
2898 schedule_act(game, 10);
2899@@ -2984,21 +2984,21 @@
2900 }
2901
2902 void Worker::draw_inner(const EditorGameBase& game,
2903- const Vector2f& point_on_dst,
2904+ const Vector2f& point_on_dst, const Coords& coords,
2905 const float scale,
2906 RenderTarget* dst) const {
2907 assert(get_owner() != nullptr);
2908 const RGBColor& player_color = get_owner()->get_playercolor();
2909
2910- dst->blit_animation(point_on_dst, scale, get_current_anim(),
2911- game.get_gametime() - get_animstart(), &player_color);
2912+ dst->blit_animation(
2913+ point_on_dst, coords, scale, get_current_anim(), game.get_gametime() - get_animstart(), &player_color);
2914
2915 if (WareInstance const* const carried_ware = get_carried_ware(game)) {
2916 const Vector2f hotspot = descr().ware_hotspot().cast<float>();
2917 const Vector2f location(
2918 point_on_dst.x - hotspot.x * scale, point_on_dst.y - hotspot.y * scale);
2919 dst->blit_animation(
2920- location, scale, carried_ware->descr().get_animation("idle"), 0, &player_color);
2921+ location, Widelands::Coords::null(), scale, carried_ware->descr().get_animation("idle"), 0, &player_color);
2922 }
2923 }
2924
2925@@ -3008,12 +3008,13 @@
2926 void Worker::draw(const EditorGameBase& egbase,
2927 const TextToDraw&,
2928 const Vector2f& field_on_dst,
2929+ const Widelands::Coords& coords,
2930 const float scale,
2931 RenderTarget* dst) const {
2932 if (!get_current_anim()) {
2933 return;
2934 }
2935- draw_inner(egbase, calc_drawpos(egbase, field_on_dst, scale), scale, dst);
2936+ draw_inner(egbase, calc_drawpos(egbase, field_on_dst, scale), coords, scale, dst);
2937 }
2938
2939 /*
2940
2941=== modified file 'src/logic/map_objects/tribes/worker.h'
2942--- src/logic/map_objects/tribes/worker.h 2019-02-27 17:19:00 +0000
2943+++ src/logic/map_objects/tribes/worker.h 2019-04-23 16:25:43 +0000
2944@@ -179,11 +179,13 @@
2945 virtual bool is_evict_allowed();
2946 virtual void draw_inner(const EditorGameBase& game,
2947 const Vector2f& point_on_dst,
2948+ const Widelands::Coords& coords,
2949 const float scale,
2950 RenderTarget* dst) const;
2951 void draw(const EditorGameBase&,
2952 const TextToDraw& draw_text,
2953 const Vector2f& field_on_dst,
2954+ const Widelands::Coords& coords,
2955 float scale,
2956 RenderTarget* dst) const override;
2957 void init_auto_task(Game&) override;
2958
2959=== modified file 'src/logic/map_objects/tribes/worker_program.cc'
2960--- src/logic/map_objects/tribes/worker_program.cc 2019-02-23 11:00:49 +0000
2961+++ src/logic/map_objects/tribes/worker_program.cc 2019-04-23 16:25:43 +0000
2962@@ -160,7 +160,7 @@
2963 harvest = {
2964 "findobject=attrib:ripe_wheat radius:2",
2965 "walk=object",
2966- "playsound=sound/farm scythe 220",
2967+ "playsound=sound/farm/scythe 220",
2968 "animate=harvesting 10000",
2969 "callobject=harvest",
2970 "animate=gathering 4000",
2971@@ -194,10 +194,10 @@
2972 fish = {
2973 "findspace=size:any radius:7 resource:fish",
2974 "walk=coords",
2975- "playsound=sound/fisher fisher_throw_net 192",
2976+ "playsound=sound/fisher/fisher_throw_net 192",
2977 "mine=fish 1", -- Remove a fish in an area of 1
2978 "animate=fishing 3000",
2979- "playsound=sound/fisher fisher_pull_net 192",
2980+ "playsound=sound/fisher/fisher_pull_net 192",
2981 "createware=fish",
2982 "return"
2983 },
2984@@ -273,7 +273,7 @@
2985 "findobject=attrib:rocks radius:6", -- Find rocks on the map within a radius of 6 from your
2986 building
2987 "walk=object", -- Now walk to those rocks
2988- "playsound=sound/atlanteans/cutting stonecutter 192",
2989+ "playsound=sound/atlanteans/cutting/stonecutter 192",
2990 "animate=hacking 12000",
2991 "callobject=shrink",
2992 "createware=granite",
2993@@ -515,7 +515,7 @@
2994 "walk=object-or-coords", -- Walk to coordinates from 1. or to object from 2.
2995 -- 2. This will create an object for us if we don't have one yet
2996 "plant=attrib:shipconstruction unless object",
2997- "playsound=sound/sawmill sawmill 230",
2998+ "playsound=sound/sawmill/sawmill 230",
2999 "animate=work 500",
3000 "construct", -- 1. This will find a space for us if no object has been planted yet
3001 "animate=work 5000",
3002@@ -618,9 +618,9 @@
3003 harvest = {
3004 "findobject=attrib:tree radius:10",
3005 "walk=object",
3006- "playsound=sound/woodcutting fast_woodcutting 250",
3007+ "playsound=sound/woodcutting/fast_woodcutting 250",
3008 "animate=hacking 10000",
3009- "playsound=sound/woodcutting tree-falling 130",
3010+ "playsound=sound/woodcutting/tree-falling 130",
3011 "callobject=fall", -- Cause the tree to fall
3012 "animate=idle 2000",
3013 "createware=log",
3014@@ -676,7 +676,7 @@
3015 "walk=object-or-coords",
3016 -- Only create a shipconstruction if we don't already have one
3017 "plant=attrib:shipconstruction unless object",
3018- "playsound=sound/sawmill sawmill 230",
3019+ "playsound=sound/sawmill/sawmill 230",
3020 "animate=work 500",
3021 "construct",
3022 "animate=work 5000",
3023@@ -830,7 +830,7 @@
3024 search = {
3025 "animate=hacking 5000",
3026 "animate=idle 2000",
3027- "playsound=sound/hammering geologist_hammer 192",
3028+ "playsound=sound/hammering/geologist_hammer 192",
3029 "animate=hacking 3000",
3030 -- Plant a resource marker at the current location, according to what has been found.
3031 "findresources"
3032@@ -875,13 +875,12 @@
3033 /* RST
3034 playsound
3035 ^^^^^^^^^^
3036-.. function:: playsound=\<sound_dir\> \<sound_name\> [priority]
3037+.. function:: playsound=\<sound_dir/sound_name\> [priority]
3038
3039- :arg string sound_dir: The directory (folder) that the sound files are in,
3040- relative to the data directory.
3041- :arg string sound_name: The name of the particular sound to play.
3042+ :arg string sound_dir/sound_name: The directory (folder) that the sound files are in,
3043+ relative to the data directory, followed by the name of the particular sound to play.
3044 There can be multiple sound files to select from at random, e.g.
3045- for `scythe`, we can have `scythe_00.ogg`, `scythe_01.ogg` ...
3046+ for `sound/farm/scythe`, we can have `sound/farm/scythe_00.ogg`, `sound/farm/scythe_01.ogg` ...
3047
3048 :arg int priority: The priority to give this sound. Maximum priority is 255.
3049
3050@@ -890,7 +889,7 @@
3051 harvest = {
3052 "findobject=attrib:ripe_wheat radius:2",
3053 "walk=object",
3054- "playsound=sound/farm scythe 220", -- Almost certainly play a swishy harvesting sound
3055+ "playsound=sound/farm/scythe 220", -- Almost certainly play a swishy harvesting sound
3056 "animate=harvesting 10000",
3057 "callobject=harvest",
3058 "animate=gathering 4000",
3059@@ -902,13 +901,14 @@
3060 if (cmd.size() < 3 || cmd.size() > 4)
3061 throw wexception("Usage: playsound <sound_dir> <sound_name> [priority]");
3062
3063- act->sparam1 = cmd[1] + "/" + cmd[2];
3064-
3065- g_sound_handler.load_fx_if_needed(cmd[1], cmd[2], act->sparam1);
3066+ act->iparam2 = SoundHandler::register_fx(SoundType::kAmbient, cmd[1]);
3067
3068 act->function = &Worker::run_playsound;
3069- act->iparam1 = cmd.size() == 3 ? 64 : // 50% chance to play, only one instance at a time
3070- atoi(cmd[3].c_str());
3071+ act->iparam1 = cmd.size() == 2 ? kFxPriorityMedium : atoi(cmd[2].c_str());
3072+ if (act->iparam1 < kFxPriorityLowest) {
3073+ throw GameDataError("Minmum priority for sounds is %d, but only %d was specified for %s",
3074+ kFxPriorityLowest, act->iparam1, cmd[1].c_str());
3075+ }
3076 }
3077
3078 /* RST
3079@@ -923,7 +923,7 @@
3080 "walk=object-or-coords", -- Walk to coordinates from 1. or to object from 2.
3081 -- 2. This will create an object for us if we don't have one yet
3082 "plant=attrib:shipconstruction unless object",
3083- "playsound=sound/sawmill sawmill 230",
3084+ "playsound=sound/sawmill/sawmill 230",
3085 "animate=work 500",
3086 -- 1. Add the current ware to the shipconstruction. This will find a space for us if no
3087 -- shipconstruction object has been planted yet
3088
3089=== modified file 'src/logic/message.h'
3090--- src/logic/message.h 2019-02-23 11:00:49 +0000
3091+++ src/logic/message.h 2019-04-23 16:25:43 +0000
3092@@ -108,7 +108,7 @@
3093 const std::string& body() const {
3094 return body_;
3095 }
3096- Widelands::Coords position() const {
3097+ const Widelands::Coords& position() const {
3098 return position_;
3099 }
3100 Widelands::Serial serial() const {
3101
3102=== modified file 'src/logic/player.cc'
3103--- src/logic/player.cc 2019-04-09 16:43:49 +0000
3104+++ src/logic/player.cc 2019-04-23 16:25:43 +0000
3105@@ -54,6 +54,7 @@
3106 #include "logic/playercommand.h"
3107 #include "scripting/lua_table.h"
3108 #include "sound/note_sound.h"
3109+#include "sound/sound_handler.h"
3110 #include "wui/interactive_player.h"
3111
3112 namespace {
3113@@ -143,7 +144,10 @@
3114 current_consumed_statistics_(the_egbase.tribes().nrwares()),
3115 ware_productions_(the_egbase.tribes().nrwares()),
3116 ware_consumptions_(the_egbase.tribes().nrwares()),
3117- ware_stocks_(the_egbase.tribes().nrwares()) {
3118+ ware_stocks_(the_egbase.tribes().nrwares()),
3119+ message_fx_(SoundHandler::register_fx(SoundType::kMessage, "sound/message")),
3120+ attack_fx_(SoundHandler::register_fx(SoundType::kMessage, "sound/military/under_attack")),
3121+ occupied_fx_(SoundHandler::register_fx(SoundType::kMessage, "sound/military/site_occupied")) {
3122 set_name(name);
3123
3124 // Disallow workers that the player's tribe doesn't have.
3125@@ -321,17 +325,20 @@
3126 * Plays the corresponding sound when a message is received and if sound is
3127 * enabled.
3128 */
3129-void Player::play_message_sound(const Message::Type& msgtype) {
3130-#define MAYBE_PLAY(type, file) \
3131- if (msgtype == type) { \
3132- Notifications::publish(NoteSound(file, 200, PRIO_ALWAYS_PLAY)); \
3133- return; \
3134- }
3135-
3136- if (g_options.pull_section("global").get_bool("sound_at_message", true)) {
3137- MAYBE_PLAY(Message::Type::kEconomySiteOccupied, "military/site_occupied")
3138- MAYBE_PLAY(Message::Type::kWarfareUnderAttack, "military/under_attack")
3139- Notifications::publish(NoteSound("message", 200, PRIO_ALWAYS_PLAY));
3140+void Player::play_message_sound(const Message* message) {
3141+ if (g_sh->is_sound_enabled(SoundType::kMessage)) {
3142+ FxId fx;
3143+ switch (message->type()) {
3144+ case Message::Type::kEconomySiteOccupied:
3145+ fx = occupied_fx_;
3146+ break;
3147+ case Message::Type::kWarfareUnderAttack:
3148+ fx = attack_fx_;
3149+ break;
3150+ default:
3151+ fx = message_fx_;
3152+ }
3153+ Notifications::publish(NoteSound(SoundType::kMessage, fx, message->position(), kFxPriorityAlwaysPlay));
3154 }
3155 }
3156
3157@@ -348,7 +355,7 @@
3158 // Sound & popup
3159 if (InteractivePlayer* const iplayer = game.get_ipl()) {
3160 if (&iplayer->player() == this) {
3161- play_message_sound(message->type());
3162+ play_message_sound(message);
3163 if (popup)
3164 iplayer->popup_message(id, *message);
3165 }
3166
3167=== modified file 'src/logic/player.h'
3168--- src/logic/player.h 2019-03-09 08:58:52 +0000
3169+++ src/logic/player.h 2019-04-23 16:25:43 +0000
3170@@ -37,6 +37,7 @@
3171 #include "logic/message_queue.h"
3172 #include "logic/see_unsee_node.h"
3173 #include "logic/widelands.h"
3174+#include "sound/constants.h"
3175
3176 class Node;
3177 namespace Widelands {
3178@@ -609,7 +610,7 @@
3179 BuildingStatsVector* get_mutable_building_statistics(const DescriptionIndex& i);
3180 void update_building_statistics(Building&, NoteImmovable::Ownership ownership);
3181 void update_team_players();
3182- void play_message_sound(const Message::Type& msgtype);
3183+ void play_message_sound(const Message* message);
3184 void enhance_or_dismantle(Building*, DescriptionIndex index_of_new_building);
3185
3186 // Called when a node becomes seen or has changed. Discovers the node and
3187@@ -682,6 +683,10 @@
3188
3189 PlayerBuildingStats building_stats_;
3190
3191+ FxId message_fx_;
3192+ FxId attack_fx_;
3193+ FxId occupied_fx_;
3194+
3195 DISALLOW_COPY_AND_ASSIGN(Player);
3196 };
3197
3198
3199=== modified file 'src/sound/CMakeLists.txt'
3200--- src/sound/CMakeLists.txt 2017-06-11 08:27:02 +0000
3201+++ src/sound/CMakeLists.txt 2019-04-23 16:25:43 +0000
3202@@ -5,8 +5,14 @@
3203 DEPENDS
3204 logic_widelands_geometry
3205 notifications
3206+ sound_constants
3207 )
3208
3209+wl_library(sound_constants
3210+ SRCS
3211+ constants.cc
3212+ constants.h
3213+)
3214
3215 wl_library(sound
3216 SRCS
3217@@ -25,6 +31,8 @@
3218 helper
3219 io_fileread
3220 io_filesystem
3221+ logic_exceptions
3222 profile
3223 random
3224+ sound_constants
3225 )
3226
3227=== added file 'src/sound/constants.cc'
3228--- src/sound/constants.cc 1970-01-01 00:00:00 +0000
3229+++ src/sound/constants.cc 2019-04-23 16:25:43 +0000
3230@@ -0,0 +1,1 @@
3231+// CMake cannot deal with header only libraries, therefore we need an empty cc file :(.
3232
3233=== added file 'src/sound/constants.h'
3234--- src/sound/constants.h 1970-01-01 00:00:00 +0000
3235+++ src/sound/constants.h 2019-04-23 16:25:43 +0000
3236@@ -0,0 +1,65 @@
3237+/*
3238+ * Copyright (C) 2006-2019 by the Widelands Development Team
3239+ *
3240+ * This program is free software; you can redistribute it and/or
3241+ * modify it under the terms of the GNU General Public License
3242+ * as published by the Free Software Foundation; either version 2
3243+ * of the License, or (at your option) any later version.
3244+ *
3245+ * This program is distributed in the hope that it will be useful,
3246+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3247+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3248+ * GNU General Public License for more details.
3249+ *
3250+ * You should have received a copy of the GNU General Public License
3251+ * along with this program; if not, write to the Free Software
3252+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
3253+ *
3254+ */
3255+
3256+#ifndef WL_SOUND_CONSTANTS_H
3257+#define WL_SOUND_CONSTANTS_H
3258+
3259+#include <limits>
3260+#include <stdint.h>
3261+
3262+/* How important is it to play the effect even when others are running
3263+ * already?
3264+ *
3265+ * \warning DO NOT CHANGE !! The values have meaning beyond just being numbers
3266+ *
3267+ * Value 0-127: probability between 0.0 and 1.0, only one instance can
3268+ * be playing at any time
3269+ *
3270+ * Value 128-254: probability between 0.0 and 1.0, many instances can
3271+ * be playing at any time
3272+ *
3273+ * Value 255: always play; unconditional
3274+ */
3275+
3276+/// Priorities lower than this one are illegal
3277+constexpr uint8_t kFxPriorityLowest = 1;
3278+/// 50% chance to play
3279+constexpr uint8_t kFxPriorityMedium = 64;
3280+/// Sounds with priority lower than this one are only allowed to play one instance at a time
3281+constexpr uint8_t kFxPriorityAllowMultiple = 128;
3282+/// Sound will always play
3283+constexpr uint8_t kFxPriorityAlwaysPlay = 255;
3284+
3285+constexpr int32_t kStereoLeft = 0;
3286+constexpr int32_t kStereoCenter = 128;
3287+constexpr int32_t kStereoRight = 254;
3288+
3289+using FxId = uint16_t;
3290+constexpr FxId kNoSoundEffect = std::numeric_limits<uint16_t>::max();
3291+
3292+/// Categorize sound effects and music to control their volume etc.
3293+enum class SoundType {
3294+ kUI,
3295+ kMessage,
3296+ kChat,
3297+ kAmbient,
3298+ kMusic
3299+};
3300+
3301+#endif // end of include guard: WL_SOUND_CONSTANTS_H
3302
3303=== modified file 'src/sound/fxset.cc'
3304--- src/sound/fxset.cc 2019-02-23 11:00:49 +0000
3305+++ src/sound/fxset.cc 2019-04-23 16:25:43 +0000
3306@@ -22,13 +22,45 @@
3307 #include <cassert>
3308
3309 #include <SDL.h>
3310-
3311-#include "sound/sound_handler.h"
3312-
3313-/** Create an FXset and set it's \ref priority_
3314- * \param[in] prio The desired priority (optional)
3315+#include <boost/regex.hpp>
3316+
3317+#include "base/log.h"
3318+#include "helper.h"
3319+#include "io/fileread.h"
3320+#include "io/filesystem/layered_filesystem.h"
3321+#include "logic/game_data_error.h"
3322+
3323+/**
3324+ * Create an FXset
3325+ * \param path The directory the sound files are in, followed by the filename base
3326+ * \param random: Randomize the time last played a bit to prevent sound onslaught at game start
3327 */
3328-FXset::FXset(uint8_t const priority) : last_used_(0), priority_(priority) {
3329+FXset::FXset(const std::string& path, uint32_t random) : last_used_(random % 2000) {
3330+ // Check directory
3331+ std::string directory = FileSystem::fs_dirname(path);
3332+ if (!g_fs->is_directory(directory)) {
3333+ throw Widelands::GameDataError("SoundHandler: Can't load files from %s, not a directory!", directory.c_str());
3334+ }
3335+
3336+ // Find files
3337+ std::string base_filename = FileSystem::fs_filename(path.c_str());
3338+ boost::regex re(base_filename + "_\\d+\\.ogg");
3339+ paths_ = filter(g_fs->list_directory(directory), [&re](const std::string& fn) {
3340+ return boost::regex_match(FileSystem::fs_filename(fn.c_str()), re);
3341+ });
3342+
3343+ // Ensure that we have at least 1 file
3344+ if (paths_.empty()) {
3345+ throw Widelands::GameDataError("FXset: No files matching the pattern '%s_<numbers>.ogg' found in directory %s\n",
3346+ base_filename.c_str(), directory.c_str());
3347+ }
3348+
3349+#ifndef NDEBUG
3350+ // Ensure that we haven't found any directories by mistake
3351+ for (const std::string& p : paths_) {
3352+ assert(!g_fs->is_directory(p));
3353+ }
3354+#endif
3355 }
3356
3357 /// Delete all fxs to avoid memory leaks. This also frees the audio data.
3358@@ -43,27 +75,48 @@
3359 fxs_.clear();
3360 }
3361
3362-/** Append a sound effect to the end of the fxset
3363- * \param[in] fx The sound fx to append
3364- * \param[in] prio Set previous \ref priority_ to new value (optional)
3365- */
3366-void FXset::add_fx(Mix_Chunk* const fx, uint8_t const prio) {
3367- assert(fx);
3368-
3369- priority_ = prio;
3370- fxs_.push_back(fx);
3371+uint32_t FXset::ticks_since_last_play() const {
3372+ return SDL_GetTicks() - last_used_;
3373 }
3374
3375-/** Get a sound effect from the fxset. \e Which variant of the fx is actually
3376- * given out is determined at random
3377- * \return a pointer to the chosen effect; 0 if sound effects are
3378- * disabled or no fx is registered
3379- */
3380-Mix_Chunk* FXset::get_fx() {
3381- if (g_sound_handler.get_disable_fx() || fxs_.empty())
3382+Mix_Chunk* FXset::get_fx(uint32_t random) {
3383+ if (!paths_.empty()) {
3384+ // Load sounds from paths if this FX hasn't been played yet
3385+ for (const std::string& path : paths_) {
3386+ load_sound_file(path);
3387+ }
3388+ assert(fxs_.size() == paths_.size());
3389+ // We don't need the paths any more
3390+ paths_.clear();
3391+ }
3392+
3393+ assert(paths_.empty());
3394+
3395+ if (fxs_.empty()) {
3396 return nullptr;
3397+ }
3398+
3399+ assert(!fxs_.empty());
3400
3401 last_used_ = SDL_GetTicks();
3402
3403- return fxs_.at(g_sound_handler.rng_.rand() % fxs_.size());
3404+ return fxs_.at(random % fxs_.size());
3405+}
3406+
3407+void FXset::load_sound_file(const std::string& path) {
3408+ FileRead fr;
3409+ if (!fr.try_open(*g_fs, path)) {
3410+ log("WARNING: Could not open %s for reading!\n", path.c_str());
3411+ return;
3412+ }
3413+
3414+ if (Mix_Chunk* const m =
3415+ Mix_LoadWAV_RW(SDL_RWFromMem(fr.data(fr.get_size(), 0), fr.get_size()), 1)) {
3416+ // Append a sound effect to the end of the fxset
3417+ assert(m);
3418+ fxs_.push_back(m);
3419+ } else {
3420+ log("FXset: loading sound effect file \"%s\" failed: %s\n",
3421+ path.c_str(), Mix_GetError());
3422+ }
3423 }
3424
3425=== modified file 'src/sound/fxset.h'
3426--- src/sound/fxset.h 2019-02-23 11:00:49 +0000
3427+++ src/sound/fxset.h 2019-04-23 16:25:43 +0000
3428@@ -20,61 +20,58 @@
3429 #ifndef WL_SOUND_FXSET_H
3430 #define WL_SOUND_FXSET_H
3431
3432+#include <set>
3433+#include <string>
3434 #include <vector>
3435
3436 #include <SDL_mixer.h>
3437
3438-class SoundHandler;
3439-
3440-/// Predefined priorities for easy reading
3441-/// \warning DO NOT CHANGE !! The values have meaning beyond just being numbers
3442-
3443-// TODO(unknown): These values should not have any meaning beyond just being numbers.
3444-
3445-#define PRIO_ALWAYS_PLAY 255
3446-#define PRIO_ALLOW_MULTIPLE 128
3447-#define PRIO_MEDIUM 63
3448-
3449 /** A collection of several sound effects meant for the same event.
3450 *
3451 * An FXset encapsulates a number of interchangeable sound effects, e.g.
3452 * all effects that might be played when a blacksmith is happily hammering away.
3453- * It is possible to select the effects one after another or in random order.
3454 * The fact that an FXset really contains several different effects is hidden
3455 * from the outside
3456 */
3457 struct FXset {
3458- friend class SoundHandler;
3459- explicit FXset(uint8_t priority = PRIO_MEDIUM);
3460+ explicit FXset(const std::string& path, uint32_t random);
3461 ~FXset();
3462
3463- void add_fx(Mix_Chunk* fx, uint8_t prio = PRIO_MEDIUM);
3464- Mix_Chunk* get_fx();
3465- bool empty() {
3466- return fxs_.empty();
3467- }
3468-
3469-protected:
3470- /// The collection of sound effects
3471- std::vector<Mix_Chunk*> fxs_;
3472+ /**
3473+ * Number of ticks since this FXSet was last played
3474+ */
3475+ uint32_t ticks_since_last_play() const;
3476+
3477+ /** Get a sound effect from the fxset. Load the audio on demand.
3478+ * \param random A random number for picking a variant
3479+ * \return a pointer to the chosen effect; 0 if sound effects are
3480+ * disabled or no fx is registered
3481+ */
3482+ Mix_Chunk* get_fx(uint32_t random);
3483+
3484+private:
3485+ /** Load an audio file into memory.
3486+ * \param path the effect to be loaded
3487+ * The file format must be ogg. Otherwise this call will complain and
3488+ * not load the file.
3489+ * \note The complete audio file will be loaded into memory and stays there
3490+ * until the game is finished.
3491+ */
3492+ void load_sound_file(const std::string& path);
3493
3494 /** When the effect was played the last time (milliseconds since SDL
3495 * initialization). Set via SDL_GetTicks()
3496 */
3497 uint32_t last_used_;
3498
3499- /** How important is it to play the effect even when others are running
3500- * already?
3501- *
3502- * Value 0-127: probability between 0.0 and 1.0, only one instance can
3503- * be playing at any time
3504- *
3505- * Value 128-254: probability between 0.0 and 1.0, many instances can
3506- * be playing at any time
3507- *
3508- * Value 255: always play; unconditional
3509- */
3510- uint8_t priority_;
3511+ /**
3512+ * Filename paths for the physical sound files
3513+ * This will be cleared when the effects have been loaded into memory by \ref get_fx on first play.
3514+ */
3515+ std::set<std::string> paths_;
3516+
3517+ /// The collection of sound effects, to be loaded on demand
3518+ std::vector<Mix_Chunk*> fxs_;
3519 };
3520
3521 #endif // end of include guard: WL_SOUND_FXSET_H
3522
3523=== modified file 'src/sound/note_sound.h'
3524--- src/sound/note_sound.h 2019-02-23 11:00:49 +0000
3525+++ src/sound/note_sound.h 2019-04-23 16:25:43 +0000
3526@@ -25,25 +25,20 @@
3527 #include "logic/widelands_geometry.h"
3528 #include "notifications/note_ids.h"
3529 #include "notifications/notifications.h"
3530+#include "sound/constants.h"
3531
3532 struct NoteSound {
3533 CAN_BE_SENT_AS_NOTE(NoteId::Sound)
3534- const std::string fx;
3535+ const SoundType type;
3536+ const FxId fx;
3537 const Widelands::Coords coords;
3538 const uint8_t priority;
3539- const uint32_t stereo_position;
3540
3541- NoteSound(const std::string& init_fx, Widelands::Coords init_coords, uint8_t init_priority)
3542- : fx(init_fx),
3543+ NoteSound(SoundType init_type, FxId init_fx, Widelands::Coords init_coords, uint8_t init_priority)
3544+ : type(init_type),
3545+ fx(init_fx),
3546 coords(init_coords),
3547- priority(init_priority),
3548- stereo_position(std::numeric_limits<uint32_t>::max()) {
3549- }
3550- NoteSound(const std::string& init_fx, uint32_t init_stereo_position, uint8_t init_priority)
3551- : fx(init_fx),
3552- coords(Widelands::Coords::null()),
3553- priority(init_priority),
3554- stereo_position(init_stereo_position) {
3555+ priority(init_priority) {
3556 }
3557 };
3558
3559
3560=== modified file 'src/sound/songset.cc'
3561--- src/sound/songset.cc 2019-02-23 11:00:49 +0000
3562+++ src/sound/songset.cc 2019-04-23 16:25:43 +0000
3563@@ -21,13 +21,26 @@
3564
3565 #include <utility>
3566
3567+#include <boost/algorithm/string/predicate.hpp>
3568+#include <boost/regex.hpp>
3569+
3570 #include "base/log.h"
3571+#include "helper.h"
3572 #include "io/fileread.h"
3573 #include "io/filesystem/layered_filesystem.h"
3574-#include "sound/sound_handler.h"
3575-
3576-/// Prepare infrastructure for reading song files from disk
3577-Songset::Songset() : m_(nullptr), rwops_(nullptr) {
3578+
3579+/// Prepare infrastructure for reading song files from disk and register the matching files
3580+Songset::Songset(const std::string& dir, const std::string& basename) : m_(nullptr), rwops_(nullptr) {
3581+ assert(g_fs);
3582+ FilenameSet files = filter(g_fs->list_directory(dir), [&basename](const std::string& fn) {
3583+ const std::string only_filename = FileSystem::fs_filename(fn.c_str());
3584+ return boost::starts_with(only_filename, basename) && boost::ends_with(only_filename, ".ogg");
3585+ });
3586+
3587+ for (const std::string& filename : files) {
3588+ assert(!g_fs->is_directory(filename));
3589+ add_song(filename);
3590+ }
3591 }
3592
3593 /// Close and delete all songs to avoid memory leaks.
3594@@ -54,25 +67,22 @@
3595 current_song_ = 0;
3596 }
3597
3598-/** Get a song from the songset. Depending on
3599- * \ref SoundHandler::sound_random_order, the selection will either be random
3600- * or linear (after last song, will start again with first).
3601- * \return a pointer to the chosen song; 0 if none was found, music is disabled
3602+/**
3603+ * Uses a 'random' number to select a song and return its audio data.
3604+ * \param random A random number for picking the song
3605+ * \return a pointer to the chosen song; nullptr if none was found
3606 * or an error occurred
3607 */
3608-Mix_Music* Songset::get_song() {
3609+Mix_Music* Songset::get_song(uint32_t random) {
3610 std::string filename;
3611
3612- if (g_sound_handler.get_disable_music() || songs_.empty())
3613+ if (songs_.empty()) {
3614 return nullptr;
3615+ }
3616
3617 if (songs_.size() > 1) {
3618- if (g_sound_handler.random_order_) {
3619- // exclude current_song from playing two times in a row
3620- current_song_ += 1 + g_sound_handler.rng_.rand() % (songs_.size() - 1);
3621- } else {
3622- ++current_song_;
3623- }
3624+ // Exclude current_song from playing two times in a row
3625+ current_song_ += 1 + random % (songs_.size() - 1);
3626 current_song_ = current_song_ % songs_.size();
3627 }
3628 filename = songs_.at(current_song_);
3629@@ -102,10 +112,10 @@
3630 m_ = Mix_LoadMUS_RW(rwops_, 0);
3631
3632 if (m_)
3633- log("SoundHandler: loaded song \"%s\"\n", filename.c_str());
3634+ log("Songset: Loaded song \"%s\"\n", filename.c_str());
3635 else {
3636- log("SoundHandler: loading song \"%s\" failed!\n", filename.c_str());
3637- log("SoundHandler: %s\n", Mix_GetError());
3638+ log("Songset: Loading song \"%s\" failed!\n", filename.c_str());
3639+ log("Songset: %s\n", Mix_GetError());
3640 }
3641
3642 return m_;
3643
3644=== modified file 'src/sound/songset.h'
3645--- src/sound/songset.h 2019-02-23 11:00:49 +0000
3646+++ src/sound/songset.h 2019-04-23 16:25:43 +0000
3647@@ -32,24 +32,20 @@
3648 *
3649 * A Songset encapsulates a number of interchangeable pieces of (background)
3650 * music, e.g. all songs that might be played while the main menu is being
3651- * shown. It is possible to access those songs one after another or in
3652- * random order. The fact that a Songset really contains several different
3653- * songs is hidden from the outside.
3654+ * shown.
3655 * A songset does not contain the audio data itself, to not use huge amounts of
3656 * memory. Instead, each song is loaded on request and the data is free()d
3657 * afterwards
3658 */
3659 struct Songset {
3660- Songset();
3661+ explicit Songset(const std::string& dir, const std::string& basename);
3662 ~Songset();
3663
3664+ Mix_Music* get_song(uint32_t random);
3665+
3666+private:
3667 void add_song(const std::string& filename);
3668- Mix_Music* get_song();
3669- bool empty() {
3670- return songs_.empty();
3671- }
3672
3673-protected:
3674 /// The filenames of all configured songs
3675 std::vector<std::string> songs_;
3676
3677
3678=== modified file 'src/sound/sound_handler.cc'
3679--- src/sound/sound_handler.cc 2019-04-09 16:43:49 +0000
3680+++ src/sound/sound_handler.cc 2019-04-23 16:25:43 +0000
3681@@ -24,71 +24,48 @@
3682
3683 #include <SDL.h>
3684 #include <SDL_mixer.h>
3685-#include <boost/algorithm/string/predicate.hpp>
3686-#include <boost/regex.hpp>
3687 #ifdef _WIN32
3688 #include <windows.h>
3689 #endif
3690
3691 #include "base/i18n.h"
3692 #include "base/log.h"
3693-#include "helper.h"
3694-#include "io/fileread.h"
3695-#include "io/filesystem/layered_filesystem.h"
3696 #include "profile/profile.h"
3697-#include "sound/songset.h"
3698
3699 namespace {
3700-
3701 constexpr int kDefaultMusicVolume = 64;
3702 constexpr int kDefaultFxVolume = 128;
3703 constexpr int kNumMixingChannels = 32;
3704-
3705-void report_initalization_error(const char* msg) {
3706- log("WARNING: Failed to initialize sound system: %s\n", msg);
3707- return;
3708-}
3709-
3710 } // namespace
3711
3712-/** The global \ref SoundHandler object
3713- * The sound handler is a static object because otherwise it'd be quite
3714- * difficult to pass the --nosound command line option
3715- */
3716-SoundHandler g_sound_handler;
3717-
3718-/** This is just a basic constructor. The \ref SoundHandler must already exist
3719- * during command line parsing because --nosound needs to be known. At this
3720- * time, however, all other information is still unknown, so a real
3721- * initialization cannot take place.
3722- * \sa SoundHandler::init()
3723+
3724+/// The global \ref SoundHandler object
3725+SoundHandler* g_sh;
3726+
3727+bool SoundHandler::backend_is_disabled_ = false;
3728+
3729+/**
3730+ * Initialize our data structures, and if SoundHandler::is_backend_disabled() is false, initialize the SDL sound system and configure everything.
3731 */
3732 SoundHandler::SoundHandler()
3733- : nosound_(false),
3734- is_backend_disabled_(false),
3735- disable_music_(false),
3736- disable_fx_(false),
3737- music_volume_(MIX_MAX_VOLUME),
3738- fx_volume_(MIX_MAX_VOLUME),
3739- random_order_(true),
3740+ : sound_options_{
3741+{SoundType::kUI, SoundOptions(kDefaultFxVolume, "ui")},
3742+{SoundType::kMessage, SoundOptions(kDefaultFxVolume, "message")},
3743+{SoundType::kChat, SoundOptions(kDefaultFxVolume, "chat")},
3744+{SoundType::kAmbient, SoundOptions(kDefaultFxVolume, "ambient")},
3745+{SoundType::kMusic, SoundOptions(kDefaultMusicVolume, "music")}},
3746 fx_lock_(nullptr) {
3747-}
3748-
3749-/// Housekeeping: unset hooks. Audio data will be freed automagically by the
3750-/// \ref Songset and \ref FXset destructors, but not the {song|fx}sets
3751-/// themselves.
3752-SoundHandler::~SoundHandler() {
3753-}
3754-
3755-/** The real initialization for SoundHandler.
3756- *
3757- * \see SoundHandler::SoundHandler()
3758- */
3759-void SoundHandler::init() {
3760+ // Ensure that we don't lose our config for when we start with sound the next time
3761 read_config();
3762+
3763+ // No sound wanted, let's not do anything.
3764+ if (SoundHandler::is_backend_disabled()) {
3765+ return;
3766+ }
3767+
3768+ // This RNG will still be somewhat predictable, but it's just to avoid
3769+ // identical playback patterns
3770 rng_.seed(SDL_GetTicks());
3771-// This RNG will still be somewhat predictable, but it's just to avoid
3772-// identical playback patterns
3773
3774 // Windows Music has crickling inside if the buffer has another size
3775 // than 4k, but other systems work fine with less, some crash
3776@@ -105,11 +82,11 @@
3777 log("SDL version: %d.%d.%d\n", static_cast<unsigned int>(sdl_version.major),
3778 static_cast<unsigned int>(sdl_version.minor), static_cast<unsigned int>(sdl_version.patch));
3779
3780- /// SDL 2.0.6 will crash due to upstream bug:
3781- /// https://bugs.launchpad.net/ubuntu/+source/libsdl2/+bug/1722060
3782+ // SDL 2.0.6 will crash due to an upstream bug:
3783+ // https://bugs.launchpad.net/ubuntu/+source/libsdl2/+bug/1722060
3784 if (sdl_version.major == 2 && sdl_version.minor == 0 && sdl_version.patch == 6) {
3785 log("Disabled sound due to a bug in SDL 2.0.6\n");
3786- nosound_ = true;
3787+ SoundHandler::disable_backend();
3788 }
3789
3790 SDL_MIXER_VERSION(&sdl_version)
3791@@ -118,77 +95,77 @@
3792
3793 log("**** END SOUND REPORT ****\n");
3794
3795- if (nosound_) {
3796- set_disable_music(true);
3797- set_disable_fx(true);
3798- is_backend_disabled_ = true;
3799+ if (SoundHandler::is_backend_disabled()) {
3800 return;
3801 }
3802
3803 if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) {
3804- report_initalization_error(SDL_GetError());
3805+ initialization_error(SDL_GetError(), false);
3806 return;
3807 }
3808
3809 if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, bufsize) != 0) {
3810- initialization_error(Mix_GetError());
3811+ initialization_error(Mix_GetError(), true);
3812 return;
3813 }
3814
3815 constexpr int kMixInitFlags = MIX_INIT_OGG;
3816- int initted = Mix_Init(kMixInitFlags);
3817- if ((initted & kMixInitFlags) != kMixInitFlags) {
3818- initialization_error("No Ogg support in SDL_Mixer.");
3819+ int init_flags = Mix_Init(kMixInitFlags);
3820+ if ((init_flags & kMixInitFlags) != kMixInitFlags) {
3821+ initialization_error("No Ogg support in SDL_Mixer.", true);
3822 return;
3823 }
3824
3825 if (Mix_AllocateChannels(kNumMixingChannels) != kNumMixingChannels) {
3826- initialization_error(Mix_GetError());
3827+ initialization_error(Mix_GetError(), true);
3828+ return;
3829 }
3830
3831 Mix_HookMusicFinished(SoundHandler::music_finished_callback);
3832 Mix_ChannelFinished(SoundHandler::fx_finished_callback);
3833- load_system_sounds();
3834- Mix_VolumeMusic(music_volume_); // can not do this before InitSubSystem
3835+ Mix_VolumeMusic(sound_options_.at(SoundType::kMusic).volume);
3836
3837- if (fx_lock_ == nullptr)
3838+ if (fx_lock_ == nullptr) {
3839 fx_lock_ = SDL_CreateMutex();
3840-}
3841-
3842-void SoundHandler::initialization_error(const std::string& msg) {
3843- log("WARNING: Failed to initialize sound system: %s\n", msg.c_str());
3844-
3845- SDL_QuitSubSystem(SDL_INIT_AUDIO);
3846-
3847- set_disable_music(true);
3848- set_disable_fx(true);
3849- is_backend_disabled_ = true;
3850- return;
3851-}
3852-
3853-void SoundHandler::shutdown() {
3854+ }
3855+}
3856+
3857+/**
3858+ * Housekeeping: unset hooks, clear the mutex and all data structures and shut down the sound system.
3859+ * Audio data will be freed automagically by the \ref Songset and \ref FXset destructors, but not the {song|fx}sets themselves.
3860+ */
3861+SoundHandler::~SoundHandler() {
3862+ if (SDL_WasInit(SDL_INIT_AUDIO) == 0) {
3863+ return;
3864+ }
3865+
3866 Mix_ChannelFinished(nullptr);
3867 Mix_HookMusicFinished(nullptr);
3868+ stop_music();
3869+ songs_.clear();
3870+ fxs_.clear();
3871
3872 int numtimesopened, frequency, channels;
3873 uint16_t format;
3874 numtimesopened = Mix_QuerySpec(&frequency, &format, &channels);
3875- log("SoundHandler closing times %i, freq %i, format %i, chan %i\n", numtimesopened, frequency,
3876- format, channels);
3877+ log("SoundHandler: Closing %i time%s, %i Hz, format %i, %i channel%s\n",
3878+ numtimesopened, numtimesopened == 1 ? "" : "s",
3879+ frequency, format, channels, channels == 1 ? "" : "s");
3880
3881- if (!numtimesopened)
3882+ if (numtimesopened == 0) {
3883 return;
3884+ }
3885
3886 Mix_HaltChannel(-1);
3887
3888 if (SDL_InitSubSystem(SDL_INIT_AUDIO) == -1) {
3889- log("audio error %s\n", SDL_GetError());
3890+ log("SoundHandler: Audio error %s\n", SDL_GetError());
3891 }
3892
3893- log("SDL_AUDIODRIVER %s\n", SDL_GetCurrentAudioDriver());
3894+ log("SoundHandler: SDL_AUDIODRIVER %s\n", SDL_GetCurrentAudioDriver());
3895
3896 if (numtimesopened != 1) {
3897- log("PROBLEM: sound device opened multiple times, trying to close");
3898+ log("SoundHandler: PROBLEM: sound device opened multiple times, trying to close");
3899 }
3900 for (int i = 0; i < numtimesopened; ++i) {
3901 Mix_CloseAudio();
3902@@ -199,476 +176,444 @@
3903 fx_lock_ = nullptr;
3904 }
3905
3906- songs_.clear();
3907- fxs_.clear();
3908-
3909 Mix_Quit();
3910 SDL_QuitSubSystem(SDL_INIT_AUDIO);
3911 }
3912
3913-/** Read the main config file, load background music and systemwide sound fx
3914- *
3915+/// Prints an error and disables and shuts down the sound system.
3916+void SoundHandler::initialization_error(const char* const msg, bool quit_sdl) {
3917+ log("WARNING: Failed to initialize sound system: %s\n", msg);
3918+ SoundHandler::disable_backend();
3919+ if (quit_sdl) {
3920+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
3921+ }
3922+ return;
3923+}
3924+
3925+/**
3926+ * Load the sound options from g_options. If an option is not available, use the defaults set by the constructor.
3927 */
3928 void SoundHandler::read_config() {
3929- Section& s = g_options.pull_section("global");
3930-
3931- if (nosound_) {
3932- set_disable_music(true);
3933- set_disable_fx(true);
3934- } else {
3935- set_disable_music(s.get_bool("disable_music", false));
3936- set_disable_fx(s.get_bool("disable_fx", false));
3937- music_volume_ = s.get_int("music_volume", kDefaultMusicVolume);
3938- fx_volume_ = s.get_int("fx_volume", kDefaultFxVolume);
3939- }
3940-
3941- random_order_ = s.get_bool("sound_random_order", true);
3942-
3943- register_song("music", "intro");
3944- register_song("music", "menu");
3945- register_song("music", "ingame");
3946-}
3947-
3948-/** Load systemwide sound fx into memory.
3949- * \note This loads only systemwide fx. Worker/building fx will be loaded by
3950- * their respective conf-file parsers
3951- */
3952-void SoundHandler::load_system_sounds() {
3953- load_fx_if_needed("sound", "click", "click");
3954- load_fx_if_needed("sound", "create_construction_site", "create_construction_site");
3955- load_fx_if_needed("sound", "message", "message");
3956- load_fx_if_needed("sound/military", "under_attack", "military/under_attack");
3957- load_fx_if_needed("sound/military", "site_occupied", "military/site_occupied");
3958- load_fx_if_needed("sound", "lobby_chat", "lobby_chat");
3959- load_fx_if_needed("sound", "lobby_freshmen", "lobby_freshmen");
3960-}
3961-
3962-/**
3963- * Returns 'true' if the playing of sounds is disabled due to sound driver problems.
3964- */
3965-bool SoundHandler::is_backend_disabled() const {
3966- return is_backend_disabled_;
3967-}
3968-
3969-/** Load a sound effect. One sound effect can consist of several audio files
3970+ // TODO(GunChleoc): Compatibility code to avoid getting bug reports about unread sections. Remove after Build 21.
3971+ if (g_options.get_section("sound") == nullptr) {
3972+ Section& global = g_options.pull_section("global");
3973+
3974+ for (auto& option : sound_options_) {
3975+ switch (option.first) {
3976+ case SoundType::kMusic:
3977+ option.second.volume = global.get_int("music_volume", option.second.volume);
3978+ option.second.enabled = !global.get_bool("disable_music", !option.second.enabled);
3979+ break;
3980+ case SoundType::kChat:
3981+ option.second.volume = global.get_int("fx_volume", option.second.volume);
3982+ option.second.enabled = global.get_bool("sound_at_message", option.second.enabled);
3983+ break;
3984+ default:
3985+ option.second.volume = global.get_int("fx_volume", option.second.volume);
3986+ option.second.enabled = !global.get_bool("disable_fx", !option.second.enabled);
3987+ break;
3988+ }
3989+ }
3990+ save_config();
3991+ }
3992+
3993+ // This is the code that we want to keep
3994+ Section& sound = g_options.pull_section("sound");
3995+ for (auto& option : sound_options_) {
3996+ option.second.volume = sound.get_int(("volume_" + option.second.name).c_str(), option.second.volume);
3997+ option.second.enabled = sound.get_bool(("enable_" + option.second.name).c_str(), option.second.enabled);
3998+ }
3999+}
4000+
4001+/// Save the current sound options to g_options
4002+void SoundHandler::save_config() {
4003+ Section& sound = g_options.pull_section("sound");
4004+ for (auto& option : sound_options_) {
4005+ const int volume = option.second.volume;
4006+ const std::string& name = option.second.name;
4007+ const bool enabled = option.second.enabled;
4008+
4009+ const std::string enable_name = "enable_" + name;
4010+ sound.set_bool(enable_name.c_str(), enabled);
4011+
4012+ const std::string volume_name = "volume_" + name;
4013+ sound.set_int(volume_name.c_str(), volume);
4014+ }
4015+}
4016+
4017+/// Read the sound options from g_options and apply them
4018+void SoundHandler::load_config() {
4019+ read_config();
4020+ for (auto& option : sound_options_) {
4021+ set_volume(option.first, option.second.volume);
4022+ set_enable_sound(option.first, option.second.enabled);
4023+ }
4024+}
4025+
4026+/** Register a sound effect. One sound effect can consist of several audio files
4027 * named EFFECT_XX.ogg, where XX is between 00 and 99.
4028 *
4029 * Subdirectories of and files under FILENAME_XX can be named anything you want.
4030 *
4031- * \param dir The relative directory where the audio files reside in data/sound
4032- * \param filename Name from which filenames will be formed
4033- * (BASENAME_XX.ogg);
4034- * also the name used with \ref play_fx
4035- */
4036-void SoundHandler::load_fx_if_needed(const std::string& dir,
4037- const std::string& basename,
4038- const std::string& fx_name) {
4039- assert(g_fs);
4040-
4041- if (!g_fs->is_directory(dir)) {
4042- throw wexception("SoundHandler: Can't load files from %s, not a directory!", dir.c_str());
4043- }
4044-
4045- if (nosound_ || fxs_.count(fx_name) > 0)
4046- return;
4047-
4048- fxs_.insert(std::make_pair(fx_name, std::unique_ptr<FXset>(new FXset())));
4049-
4050- boost::regex re(basename + "_\\d+\\.ogg");
4051- FilenameSet files = filter(g_fs->list_directory(dir), [&re](const std::string& fn) {
4052- return boost::regex_match(FileSystem::fs_filename(fn.c_str()), re);
4053- });
4054-
4055- for (const std::string& path : files) {
4056- assert(!g_fs->is_directory(path));
4057- load_one_fx(path, fx_name);
4058- }
4059-}
4060-
4061-/** Add exactly one file to the given fxset.
4062- * \param path the effect to be loaded
4063- * \param fx_name the fxset to add the file to
4064- * The file format must be ogg. Otherwise this call will complain and
4065- * not load the file.
4066- * \note The complete audio file will be loaded into memory and stays there
4067- * until the game is finished.
4068- */
4069-void SoundHandler::load_one_fx(const std::string& path, const std::string& fx_name) {
4070- if (nosound_ || is_backend_disabled_) {
4071- return;
4072- }
4073-
4074- FileRead fr;
4075- if (!fr.try_open(*g_fs, path)) {
4076- log("WARNING: Could not open %s for reading!\n", path.c_str());
4077- return;
4078- }
4079-
4080- if (Mix_Chunk* const m =
4081- Mix_LoadWAV_RW(SDL_RWFromMem(fr.data(fr.get_size(), 0), fr.get_size()), 1)) {
4082- // Make sure that requested FXset exists
4083-
4084- assert(fxs_.count(fx_name) > 0);
4085-
4086- fxs_[fx_name]->add_fx(m);
4087- } else
4088- log("SoundHandler: loading sound effect \"%s\" for FXset \"%s\" "
4089- "failed: %s\n",
4090- path.c_str(), fx_name.c_str(), Mix_GetError());
4091-}
4092-
4093-/** Find out whether to actually play a certain effect right now or rather not
4094- * (to avoid "sonic overload").
4095- */
4096-// TODO(unknown): What is the selection algorithm? cf class documentation
4097-bool SoundHandler::play_or_not(const std::string& fx_name,
4098- int32_t const stereo_pos,
4099+ * \param type The category of the FxSet to create
4100+ * \param fx_path The relative path and base filename from which filenames will be formed
4101+ * (<datadir>/fx_path_XX.ogg). If an effect with the same 'type' and 'fx_path' already exists, we assume that it is already registered and skip it.
4102+ * \returns An ID for the effect that can be used to identify it in \ref play_fx.
4103+ */
4104+
4105+FxId SoundHandler::register_fx(SoundType type, const std::string& fx_path) {
4106+ if (SoundHandler::is_backend_disabled() || g_sh == nullptr) {
4107+ return kNoSoundEffect;
4108+ }
4109+ return g_sh->do_register_fx(type, fx_path);
4110+}
4111+
4112+/// Non-static implementation of register_fx
4113+FxId SoundHandler::do_register_fx(SoundType type, const std::string& fx_path) {
4114+ assert(!SoundHandler::is_backend_disabled());
4115+ if (fx_ids_[type].count(fx_path) == 0) {
4116+ const FxId new_id = fxs_[type].size();
4117+ fx_ids_[type].insert(std::make_pair(fx_path, new_id));
4118+ fxs_[type].insert(std::make_pair(new_id, std::unique_ptr<FXset>(new FXset(fx_path, rng_.rand()))));
4119+ return new_id;
4120+ } else {
4121+ return fx_ids_[type].at(fx_path);
4122+ }
4123+}
4124+
4125+/**
4126+ * Find out whether to actually play a certain effect right now or rather not
4127+ * (to avoid "sonic overload"). Based on priority and on when it was last played.
4128+ * System sounds and sounds with priority "kFxPriorityAlwaysPlay" always return 'true'.
4129+ */
4130+bool SoundHandler::play_or_not(SoundType type, const FxId fx_id,
4131 uint8_t const priority) {
4132- bool allow_multiple = false; // convenience for easier code reading
4133- float evaluation; // Temporary to calculate single influences
4134- float probability; // Weighted total of all influences
4135-
4136- if (nosound_)
4137- return false;
4138-
4139- // Probability that this fx gets played; initially set according to priority
4140-
4141- // float division! not integer
4142- probability = (priority % PRIO_ALLOW_MULTIPLE) / 128.0f;
4143-
4144- // TODO(unknown): what to do with fx that happen offscreen?
4145- // TODO(unknown): reduce volume? reduce priority? other?
4146- if (stereo_pos == -1) {
4147- return false;
4148- }
4149-
4150- // TODO(unknown): check for a free channel
4151-
4152- if (priority == PRIO_ALWAYS_PLAY) {
4153- // TODO(unknown): if there is no free channel, kill a running fx and complain
4154- return true;
4155- }
4156-
4157- if (priority >= PRIO_ALLOW_MULTIPLE)
4158- allow_multiple = true;
4159-
4160- // Find out if an fx called fx_name is already running
4161- bool already_running = false;
4162-
4163- // Access to active_fx_ is protected because it can
4164- // be accessed from callback
4165- if (fx_lock_)
4166- SDL_LockMutex(fx_lock_);
4167-
4168- // starting a block, so I can define a local type for iterating
4169- {
4170+ assert(!SoundHandler::is_backend_disabled() && is_sound_enabled(type));
4171+ assert(priority >= kFxPriorityLowest);
4172+
4173+ if (fxs_[type].count(fx_id) == 0) {
4174+ return false;
4175+ }
4176+
4177+ if (type != SoundType::kAmbient) {
4178+ // We always play UI, chat and system sounds
4179+ return true;
4180+ }
4181+
4182+ // We always play important sounds
4183+ if (priority == kFxPriorityAlwaysPlay) {
4184+ return true;
4185+ }
4186+
4187+ // Do not run multiple instances of the same sound effect if the priority is too low
4188+ bool too_many_playing = false;
4189+ if (priority < kFxPriorityAllowMultiple) {
4190+ lock_fx();
4191+ // Find out if an fx called 'fx_name' is already running
4192 for (const auto& fx_pair : active_fx_) {
4193- if (fx_pair.second == fx_name) {
4194- already_running = true;
4195+ if (fx_pair.second == fx_id) {
4196+ too_many_playing = true;
4197 break;
4198 }
4199 }
4200 }
4201
4202- if (fx_lock_)
4203- SDL_UnlockMutex(fx_lock_);
4204+ release_fx_lock();
4205
4206- if (!allow_multiple && already_running)
4207+ if (too_many_playing) {
4208 return false;
4209+ }
4210
4211 // TODO(unknown): long time since any play increases weighted_priority
4212 // TODO(unknown): high general frequency reduces weighted priority
4213 // TODO(unknown): deal with "coupled" effects like throw_net and retrieve_net
4214
4215- uint32_t const ticks_since_last_play = SDL_GetTicks() - fxs_[fx_name]->last_used_;
4216-
4217- // reward an fx for being silent
4218- if (ticks_since_last_play > SLIDING_WINDOW_SIZE) {
4219- evaluation = 1; // arbitrary value; 0 -> no change, 1 -> probability = 1
4220+ uint32_t const ticks_since_last_play = fxs_[type][fx_id]->ticks_since_last_play();
4221+
4222+ // Weighted total probability that this fx gets played; initially set according to priority
4223+ // float division! not integer
4224+ float probability = (priority % kFxPriorityAllowMultiple) / static_cast<float>(kFxPriorityAllowMultiple);
4225+
4226+ // How many milliseconds in the past to consider
4227+ constexpr uint32_t kSlidingWindowSize = 20000;
4228+
4229+ if (ticks_since_last_play > kSlidingWindowSize) { // reward an fx for being silent
4230+ const float evaluation = 1.0f; // arbitrary value; 0 -> no change, 1 -> probability = 1
4231
4232 // "decrease improbability"
4233- probability = 1 - ((1 - probability) * (1 - evaluation));
4234+ probability = 1.0f - ((1.0f - probability) * (1.0f - evaluation));
4235 } else { // Penalize an fx for playing in short succession
4236- evaluation = static_cast<float>(ticks_since_last_play) / SLIDING_WINDOW_SIZE;
4237+ const float evaluation = static_cast<float>(ticks_since_last_play) / kSlidingWindowSize;
4238 probability *= evaluation; // decrease probability
4239 }
4240
4241 // finally: the decision
4242 // float division! not integer
4243- return (rng_.rand() % 255) / 255.0f <= probability;
4244+ return (rng_.rand() % kFxPriorityAlwaysPlay) / static_cast<float>(kFxPriorityAlwaysPlay) <= probability;
4245 }
4246
4247-/** \overload
4248- * \param fx_name The identifying name of the sound effect, see \ref load_fx .
4249- * \param stereo_position position in widelands' game window, see
4250- * \ref stereo_position
4251+/**
4252+ * \param type The categorization of the sound effect to be played
4253+ * \param fx_id The ID of the sound effect, see \ref register_fx
4254 * \param priority How important is it that this FX actually gets
4255 * played? (see \ref FXset::priority_)
4256+ * \param stereo_position Position in widelands' game window
4257+ * \param distance Distance in widelands' game window
4258 */
4259-void SoundHandler::play_fx(const std::string& fx_name,
4260+void SoundHandler::play_fx(SoundType type, const FxId fx_id,
4261+ uint8_t const priority,
4262 int32_t const stereo_pos,
4263- uint8_t const priority) {
4264- if (nosound_ || is_backend_disabled_)
4265- return;
4266-
4267- assert(stereo_pos >= -1);
4268- assert(stereo_pos <= 254);
4269-
4270- if (get_disable_fx())
4271- return;
4272-
4273- if (fxs_.count(fx_name) == 0) {
4274- log("SoundHandler: sound effect \"%s\" does not exist!\n", fx_name.c_str());
4275+ int distance) {
4276+ if (SoundHandler::is_backend_disabled() || !is_sound_enabled(type)) {
4277+ return;
4278+ }
4279+
4280+ assert(stereo_pos >= kStereoLeft);
4281+ assert(stereo_pos <= kStereoRight);
4282+
4283+ if (fx_id == kNoSoundEffect) {
4284+ throw wexception("SoundHandler: Trying to play sound effect that was never registered. Maybe you registered it before instantiating g_sh?\n");
4285+ }
4286+
4287+ if (fxs_[type].count(fx_id) == 0) {
4288+ log("SoundHandler: Sound effect %d does not exist!\n", fx_id);
4289 return;
4290 }
4291
4292 // See if the FX should be played
4293- if (!play_or_not(fx_name, stereo_pos, priority))
4294+ if (!play_or_not(type, fx_id, priority)) {
4295 return;
4296+ }
4297
4298 // retrieve the fx and play it if it's valid
4299- if (Mix_Chunk* const m = fxs_[fx_name]->get_fx()) {
4300+ if (Mix_Chunk* const m = fxs_[type][fx_id]->get_fx(rng_.rand())) {
4301 const int32_t chan = Mix_PlayChannel(-1, m, 0);
4302 if (chan == -1) {
4303 log("SoundHandler: Mix_PlayChannel failed: %s\n", Mix_GetError());
4304 } else {
4305- Mix_SetPanning(chan, 254 - stereo_pos, stereo_pos);
4306- Mix_Volume(chan, get_fx_volume());
4307+ Mix_SetPanning(chan, kStereoRight - stereo_pos, stereo_pos);
4308+ Mix_SetDistance(chan, distance);
4309+ Mix_Volume(chan, get_volume(type));
4310
4311- // Access to active_fx_ is protected
4312- // because it can be accessed from callback
4313- if (fx_lock_)
4314- SDL_LockMutex(fx_lock_);
4315- active_fx_[chan] = fx_name;
4316- if (fx_lock_)
4317- SDL_UnlockMutex(fx_lock_);
4318+ lock_fx();
4319+ active_fx_[chan] = fx_id;
4320+ release_fx_lock();
4321 }
4322- } else
4323- log("SoundHandler: sound effect \"%s\" exists but contains no files!\n", fx_name.c_str());
4324-}
4325-
4326-/** Load a background song. One "song" can consist of several audio files named
4327+ } else {
4328+ log("SoundHandler: Sound effect %d exists but contains no files!\n", fx_id);
4329+ }
4330+}
4331+
4332+/// Removes the given FXset from memory
4333+void SoundHandler::remove_fx_set(SoundType type) {
4334+ fxs_.erase(type);
4335+ fx_ids_.erase(type);
4336+}
4337+
4338+/**
4339+ * Register a background songset. A songset can consist of several audio files named
4340 * FILE_XX.ogg, where XX is between 00 and 99.
4341 * \param dir The directory where the audio files reside.
4342 * \param basename Name from which filenames will be formed
4343- * (BASENAME_XX.ogg); also the name used with \ref play_fx .
4344- * This just registers the song, actual loading takes place when
4345- * \ref Songset::get_song() is called, i.e. when the song is about to be
4346+ * (BASENAME_XX.ogg); also the name used with \ref change_music .
4347+ * This just registers the songs, actual loading takes place when
4348+ * \ref Songset::get_song() is called, i.e. when a song is about to be
4349 * played. The song will automatically be removed from memory when it has
4350 * finished playing.
4351 */
4352-void SoundHandler::register_song(const std::string& dir, const std::string& basename) {
4353- if (nosound_ || is_backend_disabled_)
4354+void SoundHandler::register_songs(const std::string& dir, const std::string& basename) {
4355+ if (SoundHandler::is_backend_disabled()) {
4356 return;
4357- assert(g_fs);
4358-
4359- FilenameSet files;
4360-
4361- files = filter(g_fs->list_directory(dir), [&basename](const std::string& fn) {
4362- const std::string only_filename = FileSystem::fs_filename(fn.c_str());
4363- return boost::starts_with(only_filename, basename) && boost::ends_with(only_filename, ".ogg");
4364- });
4365-
4366- for (const std::string& filename : files) {
4367- assert(!g_fs->is_directory(filename));
4368- if (songs_.count(basename) == 0) {
4369- songs_.insert(std::make_pair(basename, std::unique_ptr<Songset>(new Songset())));
4370- }
4371- songs_[basename]->add_song(filename);
4372+ }
4373+ if (songs_.count(basename) == 0) {
4374+ songs_.insert(std::make_pair(basename, std::unique_ptr<Songset>(new Songset(dir, basename))));
4375 }
4376 }
4377
4378-/** Start playing a songset.
4379+/**
4380+ * Start playing a songset.
4381 * \param songset_name The songset to play a song from.
4382- * \param fadein_ms Song will fade from 0% to 100% during fadein_ms
4383- * milliseconds starting from now.
4384- * \note When calling start_music() while music is still fading out from
4385- * \ref stop_music()
4386- * or \ref change_music() this function will block until the fadeout is complete
4387+ * \note When calling start_music() while music is still fading out from \ref stop_music() or \ref change_music(),
4388+ * this function will block until the fadeout is complete
4389 */
4390-void SoundHandler::start_music(const std::string& songset_name, int32_t fadein_ms) {
4391- if (get_disable_music() || nosound_ || is_backend_disabled_)
4392+void SoundHandler::start_music(const std::string& songset_name) {
4393+ if (SoundHandler::is_backend_disabled() || !is_sound_enabled(SoundType::kMusic)) {
4394 return;
4395-
4396- if (fadein_ms == 0)
4397- fadein_ms = 250; // avoid clicks
4398-
4399- if (Mix_PlayingMusic())
4400- change_music(songset_name, 0, fadein_ms);
4401-
4402- if (songs_.count(songset_name) == 0)
4403+ }
4404+
4405+ if (Mix_PlayingMusic()) {
4406+ change_music(songset_name, kMinimumMusicFade);
4407+ }
4408+
4409+ if (songs_.count(songset_name) == 0) {
4410 log("SoundHandler: songset \"%s\" does not exist!\n", songset_name.c_str());
4411- else {
4412- if (Mix_Music* const m = songs_[songset_name]->get_song()) {
4413- Mix_FadeInMusic(m, 1, fadein_ms);
4414+ } else {
4415+ if (Mix_Music* const m = songs_[songset_name]->get_song(rng_.rand())) {
4416+ Mix_FadeInMusic(m, 1, kMinimumMusicFade);
4417 current_songset_ = songset_name;
4418- } else
4419+ } else {
4420 log("SoundHandler: songset \"%s\" exists but contains no files!\n", songset_name.c_str());
4421+ }
4422 }
4423- Mix_VolumeMusic(music_volume_);
4424 }
4425
4426-/** Stop playing a songset.
4427+/**
4428+ * Stop playing a songset.
4429 * \param fadeout_ms Song will fade from 100% to 0% during fadeout_ms
4430 * milliseconds starting from now.
4431 */
4432-void SoundHandler::stop_music(int32_t fadeout_ms) {
4433- if (get_disable_music() || nosound_)
4434+void SoundHandler::stop_music(int fadeout_ms) {
4435+ if (SoundHandler::is_backend_disabled()) {
4436 return;
4437-
4438- if (fadeout_ms == 0)
4439- fadeout_ms = 250; // avoid clicks
4440-
4441- Mix_FadeOutMusic(fadeout_ms);
4442+ }
4443+
4444+ if (Mix_PlayingMusic()) {
4445+ Mix_FadeOutMusic(std::max(fadeout_ms, kMinimumMusicFade));
4446+ }
4447 }
4448
4449-/** Play an other piece of music.
4450+/**
4451+ * Play a new piece of music.
4452 * This is a member function provided for convenience. It is a wrapper around
4453 * \ref start_music and \ref stop_music.
4454 * \param fadeout_ms Old song will fade from 100% to 0% during fadeout_ms
4455 * milliseconds starting from now.
4456- * \param fadein_ms New song will fade from 0% to 100% during fadein_ms
4457- * milliseconds starting from now.
4458 * If songset_name is empty, another song from the currently active songset will
4459 * be selected
4460 */
4461 void SoundHandler::change_music(const std::string& songset_name,
4462- int32_t const fadeout_ms,
4463- int32_t const fadein_ms) {
4464- if (nosound_)
4465+ int const fadeout_ms) {
4466+ if (SoundHandler::is_backend_disabled()) {
4467 return;
4468-
4469- std::string s = songset_name;
4470-
4471- if (s == "")
4472- s = current_songset_;
4473- else
4474- current_songset_ = s;
4475-
4476- if (Mix_PlayingMusic())
4477+ }
4478+
4479+ if (!songset_name.empty()) {
4480+ current_songset_ = songset_name;
4481+ }
4482+
4483+ if (Mix_PlayingMusic()) {
4484 stop_music(fadeout_ms);
4485- else
4486- start_music(s, fadein_ms);
4487-}
4488-
4489-bool SoundHandler::get_disable_music() const {
4490- return disable_music_;
4491-}
4492-bool SoundHandler::get_disable_fx() const {
4493- return disable_fx_;
4494-}
4495-int32_t SoundHandler::get_music_volume() const {
4496- return music_volume_;
4497-}
4498-int32_t SoundHandler::get_fx_volume() const {
4499- return fx_volume_;
4500-}
4501-
4502-/** Normal set_* function, but the music must be started/stopped accordingly
4503- * Also, the new value is written back to the config file right away. It might
4504- * get lost otherwise.
4505- */
4506-void SoundHandler::set_disable_music(bool const disable) {
4507- if (is_backend_disabled_ || disable_music_ == disable)
4508- return;
4509-
4510- if (disable) {
4511- stop_music();
4512- disable_music_ = true;
4513 } else {
4514- disable_music_ = false;
4515 start_music(current_songset_);
4516 }
4517-
4518- g_options.pull_section("global").set_bool("disable_music", disable);
4519-}
4520-
4521-/** Normal set_* function
4522- * Also, the new value is written back to the config file right away. It might
4523- * get lost otherwise.
4524- */
4525-void SoundHandler::set_disable_fx(bool const disable) {
4526- if (is_backend_disabled_)
4527- return;
4528-
4529- disable_fx_ = disable;
4530-
4531- g_options.pull_section("global").set_bool("disable_fx", disable);
4532-}
4533-
4534-/**
4535- * Normal set_* function.
4536- * Set the music volume between 0 (muted) and \ref get_max_volume().
4537- * The new value is written back to the config file.
4538- *
4539- * \param volume The new music volume.
4540- */
4541-void SoundHandler::set_music_volume(int32_t volume) {
4542- if (!is_backend_disabled_ && !nosound_) {
4543- music_volume_ = volume;
4544+}
4545+
4546+/// Returns the currently playing songset
4547+const std::string SoundHandler::current_songset() const {
4548+ return current_songset_;
4549+}
4550+
4551+/// Returns whether we want to hear sounds of the given 'type'
4552+bool SoundHandler::is_sound_enabled(SoundType type) const {
4553+ assert(sound_options_.count(type) == 1);
4554+ return sound_options_.at(type).enabled;
4555+}
4556+
4557+/// Returns the volume that the given 'type' of sound is to be played at
4558+int32_t SoundHandler::get_volume(SoundType type) const {
4559+ assert(sound_options_.count(type) == 1);
4560+ return sound_options_.at(type).volume;
4561+}
4562+
4563+/**
4564+ * Sets that we want to / don't want to hear the given 'type' of sounds. If the type is \ref SoundType::kMusic, start/stop the music as well.
4565+ */
4566+void SoundHandler::set_enable_sound(SoundType type, bool const enable) {
4567+ if (SoundHandler::is_backend_disabled()) {
4568+ return;
4569+ }
4570+ assert(sound_options_.count(type) == 1);
4571+
4572+ SoundOptions& sound_options = sound_options_.at(type);
4573+ sound_options.enabled = enable;
4574+
4575+ // Special treatment for music
4576+ switch (type) {
4577+ case SoundType::kMusic:
4578+ if (enable) {
4579+ if (!Mix_PlayingMusic()) {
4580+ start_music(current_songset_);
4581+ }
4582+ } else {
4583+ stop_music();
4584+ }
4585+ break;
4586+ default:
4587+ break;
4588+ }
4589+}
4590+
4591+/**
4592+ * Sets the music or sound 'volume' for the given 'type' between 0 (muted) and \ref get_max_volume().
4593+ */
4594+void SoundHandler::set_volume(SoundType type, int32_t volume) {
4595+ if (SoundHandler::is_backend_disabled()) {
4596+ return;
4597+ }
4598+
4599+ assert(sound_options_.count(type) == 1);
4600+ assert(volume >= 0 && volume <= get_max_volume());
4601+
4602+ sound_options_.at(type).volume = volume;
4603+
4604+ // Special treatment for music
4605+ switch (type) {
4606+ case SoundType::kMusic:
4607 Mix_VolumeMusic(volume);
4608- g_options.pull_section("global").set_int("music_volume", volume);
4609- }
4610-}
4611-
4612-/**
4613- * Normal set_* function
4614- * Set the FX sound volume between 0 (muted) and \ref get_max_volume().
4615- * The new value is written back to the config file.
4616- *
4617- * \param volume The new music volume.
4618- */
4619-void SoundHandler::set_fx_volume(int32_t volume) {
4620- if (!is_backend_disabled_ && !nosound_) {
4621- fx_volume_ = volume;
4622+ break;
4623+ default:
4624 Mix_Volume(-1, volume);
4625- g_options.pull_section("global").set_int("fx_volume", volume);
4626+ break;
4627 }
4628 }
4629
4630-/** Callback to notify \ref SoundHandler that a song has finished playing.
4631- * Usually, another song from the same songset will be started.
4632- * There is a special case for the intro screen's music: only one song will be
4633- * played. If the user has not clicked the mouse or pressed escape when the song
4634- * finishes, Widelands will automatically go on to the main menu.
4635+/**
4636+ * Returns the max value for volume settings. We use a function to hide
4637+ * SDL_mixer constants outside of sound_handler.
4638+ */
4639+int32_t SoundHandler::get_max_volume() const {
4640+ return MIX_MAX_VOLUME;
4641+}
4642+
4643+/**
4644+ * Callback to notify \ref SoundHandler that a song has finished playing.
4645+ * Pushes an SDL_Event with type = SDL_USEREVENT and user.code = CHANGE_MUSIC.
4646 */
4647 void SoundHandler::music_finished_callback() {
4648 // DO NOT CALL SDL_mixer FUNCTIONS OR SDL_LockAudio FROM HERE !!!
4649
4650+ assert(!SoundHandler::is_backend_disabled());
4651+ // Trigger that we want a music change and leave the specifics to the application.
4652 SDL_Event event;
4653- if (g_sound_handler.current_songset_ == "intro") {
4654- // Special case for splashscreen: there, only one song is ever played
4655- event.type = SDL_KEYDOWN;
4656- event.key.state = SDL_PRESSED;
4657- event.key.keysym.sym = SDLK_ESCAPE;
4658- } else {
4659- // Else just play the next song - see general description for
4660- // further explanation
4661- event.type = SDL_USEREVENT;
4662- event.user.code = CHANGE_MUSIC;
4663- }
4664+ event.type = SDL_USEREVENT;
4665+ event.user.code = CHANGE_MUSIC;
4666 SDL_PushEvent(&event);
4667 }
4668
4669-/** Callback to notify \ref SoundHandler that a sound effect has finished
4670- * playing.
4671+/**
4672+ * Callback to notify \ref SoundHandler that a sound effect has finished
4673+ * playing. Removes the finished sound fx from the list of currently playing ones.
4674 */
4675 void SoundHandler::fx_finished_callback(int32_t const channel) {
4676 // DO NOT CALL SDL_mixer FUNCTIONS OR SDL_LockAudio FROM HERE !!!
4677
4678+ assert(!SoundHandler::is_backend_disabled());
4679 assert(0 <= channel);
4680- g_sound_handler.handle_channel_finished(static_cast<uint32_t>(channel));
4681+ g_sh->lock_fx();
4682+ g_sh->active_fx_.erase(static_cast<uint32_t>(channel));
4683+ g_sh->release_fx_lock();
4684 }
4685
4686-/** Remove a finished sound fx from the list of currently playing ones
4687- * This is part of \ref fx_finished_callback
4688- */
4689-void SoundHandler::handle_channel_finished(uint32_t channel) {
4690- // Needs locking because active_fx_ may be accessed
4691- // from this callback or from main thread
4692- if (fx_lock_)
4693+/// Lock the SDL mutex. Access to 'active_fx_' is protected by mutex because it can be accessed both from callbacks or from the main thread.
4694+void SoundHandler::lock_fx() {
4695+ if (fx_lock_) {
4696 SDL_LockMutex(fx_lock_);
4697- active_fx_.erase(channel);
4698- if (fx_lock_)
4699+ }
4700+}
4701+
4702+/// Release the SDL mutex
4703+void SoundHandler::release_fx_lock() {
4704+ if (fx_lock_) {
4705 SDL_UnlockMutex(fx_lock_);
4706+ }
4707 }
4708
4709=== modified file 'src/sound/sound_handler.h'
4710--- src/sound/sound_handler.h 2019-02-23 11:00:49 +0000
4711+++ src/sound/sound_handler.h 2019-04-23 16:25:43 +0000
4712@@ -30,18 +30,12 @@
4713 #include <unistd.h>
4714 #endif
4715
4716+#include <SDL_mutex.h>
4717+
4718 #include "random/random.h"
4719 #include "sound/fxset.h"
4720-
4721-struct Songset;
4722-struct SDL_mutex;
4723-class FileRead;
4724-
4725-/// How many milliseconds in the past to consider for
4726-/// SoundHandler::play_or_not()
4727-#define SLIDING_WINDOW_SIZE 20000
4728-
4729-extern class SoundHandler g_sound_handler;
4730+#include "sound/constants.h"
4731+#include "sound/songset.h"
4732
4733 /** The 'sound server' for Widelands.
4734 *
4735@@ -54,47 +48,39 @@
4736 *
4737 * Background music for different situations (e.g. 'Menu', 'Gameplay') is
4738 * collected in songsets. Each Songset contains references to one or more
4739- * songs in ogg format. The only ordering inside a soundset is from the order
4740- * in which the songs were loaded.
4741+ * songs in ogg format.
4742 *
4743 * Other classes can request to start or stop playing a certain songset,
4744 * changing the songset is provided as a convenience method. It is also
4745 * possible to switch to some other piece inside the same songset - but there
4746 * is \e no control over \e which song out of a songset gets played. The
4747- * selection is either linear (the order in which the songs were loaded) or
4748- * completely random.
4749- *
4750- * The files for the predefined system songsets
4751- * \li \c intro
4752- * \li \c menu
4753- * \li \c ingame
4754- *
4755- * must reside directly in the directory 'sounds' and must be named
4756- * SONGSET_XX.??? where XX is a number from 00 to 99 and ??? is a filename
4757- * extension. All subdirectories of 'sounds' will be considered to contain
4758- * ingame music. The the music and sub-subdirectories found in them can be
4759- * arbitrarily named. This means: everything below sound/ingame_01 can have
4760- * any name you want. All audio files below sound/ingame_01 will be played as
4761+ * selection is random.
4762+ *
4763+ * The files must reside somewhere below the datadir and must be named
4764+ * SONGSET_XX.ogg where XX is a number from 00 to 99.
4765+ * The music and sub-subdirectories found in a directory can be
4766+ * arbitrarily named. For example, if we register songsets in <datadir>/music,
4767+ * this means that everything below <datadir>/music/ingame_01 can have
4768+ * any name you want. All audio files below <datadir>/music/ingame_01 will be played as
4769 * ingame music.
4770 *
4771- * For more information about the naming scheme, see load_fx()
4772- *
4773- * You should be using the ogg format for music.
4774+ * For more information about the naming scheme, see register_songs()
4775+ *
4776+ * You must use the ogg format for music.
4777+ *
4778 *
4779 * \par Sound effects
4780 *
4781- * Buildings and workers can use sound effects in their programs. To do so, use
4782- * e.g. "playsound blacksmith_hammer" in the appropriate conf file. The conf file
4783- * parser will then load one or more audio files for 'hammering blacksmith'
4784- * from the building's/worker's configuration directory and store them in an
4785- * FXset for later access, similar to the way music is stored in songsets.
4786- * For effects, however, the selection is always random. Sound effects are kept
4787- * in memory at all times, to avoid delays from disk access.
4788- *
4789- * The abovementioned sound effects are synchronized with a work program. It's
4790- * also possible to have sound effects that are synchronized with a
4791- * building/worker \e animation. For more information about this look at class
4792- * AnimationManager.
4793+ * Use register_fx() to record the file locations for each sound effect, to be loaded on first play.
4794+ * Sound effects are kept in memory at all times once they have been loaded, to avoid delays from disk access.
4795+ * You can use \ref remove_fx_set to deregister and unload sound effects from memory though.
4796+ * The file naming scheme is the same as for the songs, and if there are multiple files for an effect, they are picked at random too.
4797+ * Sound effects are categorized into multiple SoundType categories, so that the user can control which type of sounds to hear.
4798+ *
4799+ * For map objects, the abovementioned sound effects are synchronized with a work program or a
4800+ * building/immovable/worker animation. For more information about this, look at the
4801+ * Animation class and the online scripting reference.
4802+ *
4803 *
4804 * \par Usage of callbacks
4805 *
4806@@ -107,10 +93,10 @@
4807 *
4808 * Callbacks must use global(or static) functions \e but \e not normal member
4809 * functions of a class. If you must know why: ask google. But how can a
4810- * static function share data with an instance of it's own class? Usually not at
4811+ * static function share data with an instance of its own class? Usually not at
4812 * all.
4813 *
4814- * Fortunately, g_sound_handler already is a global variable,
4815+ * Fortunately, g_sh already is a global variable,
4816 * and therefore accessible to global functions. So problem 1 disappears.
4817 *
4818 * Problem 2:
4819@@ -155,112 +141,105 @@
4820 * change_music() from inside start_music(). It really is not recursive, trust
4821 * me :-)
4822 */
4823-// TODO(unknown): DOC: priorities
4824-// TODO(unknown): DOC: play-or-not algorithm
4825-// TODO(unknown): Environmental sound effects (e.g. wind)
4826-// TODO(unknown): repair and reenable animation sound effects for 1-pic-animations
4827-// TODO(unknown): accommodate runtime changes of i18n language
4828-// TODO(unknown): accommodate sound activation if it was disabled at the beginning
4829
4830-// This is used for SDL UserEvents to be handled in the main loop.
4831+/// This is used for SDL UserEvents to be handled in the main loop.
4832 enum { CHANGE_MUSIC };
4833+
4834+/// Avoid clicks when starting/stopping music
4835+constexpr int kMinimumMusicFade = 250;
4836+
4837 class SoundHandler {
4838- friend struct Songset;
4839- friend struct FXset;
4840-
4841 public:
4842 SoundHandler();
4843 ~SoundHandler();
4844
4845- void init();
4846- void shutdown();
4847+ void save_config();
4848+ void load_config();
4849+
4850+ /**
4851+ * Returns 'true' if the playing of sounds is disabled due to sound driver problems, or because disable_backend() was used.
4852+ */
4853+ inline static bool is_backend_disabled() {
4854+ return SoundHandler::backend_is_disabled_;
4855+ }
4856+
4857+ /**
4858+ * Disables all sound.
4859+ */
4860+ inline static void disable_backend() {
4861+ SoundHandler::backend_is_disabled_ = true;
4862+ }
4863+
4864+
4865+ // This is static so that we can load the tribes and world without instantiating the sound system
4866+ static FxId register_fx(SoundType type, const std::string& fx_path);
4867+
4868+ void play_fx(SoundType type, FxId fx_id,
4869+ uint8_t priority = kFxPriorityAlwaysPlay,
4870+ int32_t stereo_position = kStereoCenter, int distance = 0);
4871+ void remove_fx_set(SoundType type);
4872+
4873+ void register_songs(const std::string& dir, const std::string& basename);
4874+ void stop_music(int fadeout_ms = kMinimumMusicFade);
4875+ void change_music(const std::string& songset_name = std::string(),
4876+ int fadeout_ms = kMinimumMusicFade);
4877+
4878+ const std::string current_songset() const;
4879+
4880+ bool is_sound_enabled(SoundType type) const;
4881+ void set_enable_sound(SoundType type, bool enable);
4882+ int32_t get_volume(SoundType type) const;
4883+ void set_volume(SoundType type, int32_t volume);
4884+
4885+ int32_t get_max_volume() const;
4886+
4887+private:
4888 void read_config();
4889- void load_system_sounds();
4890- bool is_backend_disabled() const;
4891-
4892- void load_fx_if_needed(const std::string& dir,
4893- const std::string& basename,
4894- const std::string& fx_name);
4895-
4896- void play_fx(const std::string& fx_name,
4897- int32_t stereo_position,
4898- uint8_t priority = PRIO_ALLOW_MULTIPLE + PRIO_MEDIUM);
4899-
4900- void register_song(const std::string& dir, const std::string& basename);
4901- void start_music(const std::string& songset_name, int32_t fadein_ms = 0);
4902- void stop_music(int32_t fadeout_ms = 0);
4903- void change_music(const std::string& songset_name = std::string(),
4904- int32_t fadeout_ms = 0,
4905- int32_t fadein_ms = 0);
4906+
4907+ FxId do_register_fx(SoundType type, const std::string& fx_path);
4908+
4909+ void initialization_error(const char* const msg, bool quit_sdl);
4910+
4911+ bool play_or_not(SoundType type, FxId fx_id, uint8_t priority);
4912+ void start_music(const std::string& songset_name);
4913
4914 static void music_finished_callback();
4915 static void fx_finished_callback(int32_t channel);
4916- void handle_channel_finished(uint32_t channel);
4917-
4918- bool get_disable_music() const;
4919- bool get_disable_fx() const;
4920- int32_t get_music_volume() const;
4921- int32_t get_fx_volume() const;
4922- void set_disable_music(bool disable);
4923- void set_disable_fx(bool disable);
4924- void set_music_volume(int32_t volume);
4925- void set_fx_volume(int32_t volume);
4926-
4927- /**
4928- * Return the max value for volume settings. We use a function to hide
4929- * SDL_mixer constants outside of sound_handler.
4930- */
4931- int32_t get_max_volume() const {
4932- return MIX_MAX_VOLUME;
4933- }
4934-
4935- /** Only for buffering the command line option --nosound until real initialization is done.
4936- * \see SoundHandler::SoundHandler()
4937- * \see SoundHandler::init()
4938- */
4939- // TODO(unknown): This is ugly. Find a better way to do it
4940- bool nosound_;
4941-
4942-private:
4943- // Prints an error and disables the sound system.
4944- void initialization_error(const std::string& msg);
4945-
4946- void load_one_fx(const std::string& path, const std::string& fx_name);
4947- bool play_or_not(const std::string& fx_name, int32_t stereo_position, uint8_t priority);
4948-
4949- /** Can sounds be played?
4950- * true = they mustn't be played (e.g. because hardware is missing)
4951- * false = can be played
4952- */
4953- bool is_backend_disabled_;
4954-
4955- /// Whether to disable background music
4956- bool disable_music_;
4957- /// Whether to disable sound effects
4958- bool disable_fx_;
4959- /// Volume of music (from 0 to get_max_volume())
4960- int32_t music_volume_;
4961- /// Volume of sound effects (from 0 to get_max_volume())
4962- int32_t fx_volume_;
4963-
4964- /** Whether to play music in random order
4965- * \note Sound effects will \e always be selected at random (inside
4966- * their FXset, of course.
4967- */
4968- bool random_order_;
4969+
4970+ void lock_fx();
4971+ void release_fx_lock();
4972+
4973+ /// Contains options for a sound type or the music
4974+ struct SoundOptions {
4975+ explicit SoundOptions(int vol, const std::string& savename) : enabled(true), volume(vol), name(savename) {
4976+ assert(!savename.empty());
4977+ assert(vol >= 0);
4978+ assert(vol <= MIX_MAX_VOLUME);
4979+ }
4980+
4981+ /// Whether the user wants to hear this type of sound
4982+ bool enabled;
4983+ /// Volume for sound effects or music (from 0 to get_max_volume())
4984+ int volume;
4985+ /// Name for saving
4986+ const std::string name;
4987+ };
4988+
4989+ /// Contains all options for sound types and music
4990+ std::map<SoundType, SoundOptions> sound_options_;
4991
4992 /// A collection of songsets
4993- using SongsetMap = std::map<std::string, std::unique_ptr<Songset>>;
4994- SongsetMap songs_;
4995+ std::map<std::string, std::unique_ptr<Songset>> songs_;
4996
4997 /// A collection of effect sets
4998- using FXsetMap = std::map<std::string, std::unique_ptr<FXset>>;
4999- FXsetMap fxs_;
5000+ std::map<SoundType, std::map<FxId, std::unique_ptr<FXset>>> fxs_;
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to status/vote changes: