2 * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 typedef enum _sound_flag
{
24 GD_SP_FORCE
= 1<<2, /* force restart, regardless of precedence level */
25 GD_SP_FAKE
= 1<<3, /* not real sounds. for example, diamond_random, which is changed to diamond_1, 2, 3, ... later */
28 typedef struct _sound_property
{
32 int channel
; /* channel this sound is played on. */
33 int precedence
; /* greater numbers will have precedence. */
34 GdSound replace
; /* replacement sound, if classic sounds are requested but this one is not classic */
37 static SoundProperty sound_flags
[] = {
38 { GD_S_NONE
, NULL
, GD_SP_CLASSIC
, 0, 0, 0},
40 /* channel 1 sounds. */
41 /* diamond collect sound has precedence over everything. */
42 /* CHANNEL 1 SOUNDS ARE ALWAYS RESTARTED, so no need for GD_SP_FORCE flag. */
43 { GD_S_STONE
, "stone.ogg", GD_SP_CLASSIC
, 1, 10},
44 { GD_S_NUT
, "nut.ogg", 0, 1, 8}, /* nut falling is relatively silent, so low precedence. */
45 { GD_S_NUT_CRACK
, "nut_crack.ogg", 0, 1, 12}, /* higher precedence than a stone bouncing. */
46 { GD_S_DIRT_BALL
, "dirt_ball.ogg", 0, 1, 8}, /* sligthly lower precedence, as stones and diamonds should be "louder" */
47 { GD_S_NITRO
, "nitro.ogg", 0, 1, 10},
48 { GD_S_FALLING_WALL
, "falling_wall.ogg", 0, 1, 10, GD_S_STONE
},
49 { GD_S_EXPANDING_WALL
, "expanding_wall.ogg", 0, 1, 10, GD_S_STONE
},
50 { GD_S_WALL_REAPPEAR
, "wall_reappear.ogg", 0, 1, 9},
51 { GD_S_DIAMOND_RANDOM
, NULL
, GD_SP_CLASSIC
|GD_SP_FAKE
, 1, 10},
52 { GD_S_DIAMOND_1
, "diamond_1.ogg", GD_SP_CLASSIC
, 1, 10},
53 { GD_S_DIAMOND_2
, "diamond_2.ogg", GD_SP_CLASSIC
, 1, 10},
54 { GD_S_DIAMOND_3
, "diamond_3.ogg", GD_SP_CLASSIC
, 1, 10},
55 { GD_S_DIAMOND_4
, "diamond_4.ogg", GD_SP_CLASSIC
, 1, 10},
56 { GD_S_DIAMOND_5
, "diamond_5.ogg", GD_SP_CLASSIC
, 1, 10},
57 { GD_S_DIAMOND_6
, "diamond_6.ogg", GD_SP_CLASSIC
, 1, 10},
58 { GD_S_DIAMOND_7
, "diamond_7.ogg", GD_SP_CLASSIC
, 1, 10},
59 { GD_S_DIAMOND_8
, "diamond_8.ogg", GD_SP_CLASSIC
, 1, 10},
60 { GD_S_DIAMOND_COLLECT
, "diamond_collect.ogg", GD_SP_CLASSIC
, 1, 100}, /* collect sounds have higher precedence than falling sounds and the like. */
61 { GD_S_SKELETON_COLLECT
, "skeleton_collect.ogg", 0, 1, 100, GD_S_DIAMOND_COLLECT
},
62 { GD_S_PNEUMATIC_COLLECT
, "pneumatic_collect.ogg", 0, 1, 50, GD_S_DIAMOND_RANDOM
},
63 { GD_S_BOMB_COLLECT
, "bomb_collect.ogg", 0, 1, 50, GD_S_DIAMOND_RANDOM
},
64 { GD_S_CLOCK_COLLECT
, "clock_collect.ogg", GD_SP_CLASSIC
, 1, 50},
65 { GD_S_SWEET_COLLECT
, "sweet_collect.ogg", 0, 1, 50, GD_S_NONE
},
66 { GD_S_KEY_COLLECT
, "key_collect.ogg", 0, 1, 50, GD_S_DIAMOND_RANDOM
},
67 { GD_S_DIAMOND_KEY_COLLECT
, "diamond_key_collect.ogg", 0, 1, 50, GD_S_DIAMOND_RANDOM
},
68 { GD_S_SLIME
, "slime.ogg", 0, 1, 5, GD_S_NONE
}, /* slime has lower precedence than diamond and stone falling sounds. */
69 { GD_S_LAVA
, "lava.ogg", 0, 1, 5, GD_S_NONE
}, /* lava has low precedence, too. */
70 { GD_S_REPLICATOR
, "replicator.ogg", 0, 1, 5, GD_S_NONE
},
71 { GD_S_ACID_SPREAD
, "acid_spread.ogg", 0, 1, 3, GD_S_NONE
}, /* same for acid, even lower. */
72 { GD_S_BLADDER_MOVE
, "bladder_move.ogg", 0, 1, 5, GD_S_NONE
}, /* same for bladder. */
73 { GD_S_BLADDER_CONVERT
, "bladder_convert.ogg", 0, 1, 8, GD_S_NONE
},
74 { GD_S_BLADDER_SPENDER
, "bladder_spender.ogg", 0, 1, 8, GD_S_NONE
},
75 { GD_S_BITER_EAT
, "biter_eat.ogg", 0, 1, 3, GD_S_NONE
}, /* very low precedence. biters tend to produce too much sound. */
77 /* channel2 sounds. */
78 { GD_S_DOOR_OPEN
, "door_open.ogg", GD_SP_CLASSIC
, 2, 10},
79 { GD_S_WALK_EARTH
, "walk_earth.ogg", GD_SP_CLASSIC
, 2, 10},
80 { GD_S_WALK_EMPTY
, "walk_empty.ogg", GD_SP_CLASSIC
, 2, 10},
81 { GD_S_STIRRING
, "stirring.ogg", GD_SP_CLASSIC
, 2, 10},
82 { GD_S_BOX_PUSH
, "box_push.ogg", 0, 2, 10, GD_S_STONE
},
83 { GD_S_TELEPORTER
, "teleporter.ogg", 0, 2, 10, GD_S_NONE
},
84 { GD_S_TIMEOUT_1
, "timeout_1.ogg", GD_SP_CLASSIC
, 2, 20}, /* timeout sounds have increasing precedence so they are always started */
85 { GD_S_TIMEOUT_2
, "timeout_2.ogg", GD_SP_CLASSIC
, 2, 21}, /* timeout sounds are examples which do not need "force restart" flag. */
86 { GD_S_TIMEOUT_3
, "timeout_3.ogg", GD_SP_CLASSIC
, 2, 22},
87 { GD_S_TIMEOUT_4
, "timeout_4.ogg", GD_SP_CLASSIC
, 2, 23},
88 { GD_S_TIMEOUT_5
, "timeout_5.ogg", GD_SP_CLASSIC
, 2, 24},
89 { GD_S_TIMEOUT_6
, "timeout_6.ogg", GD_SP_CLASSIC
, 2, 25},
90 { GD_S_TIMEOUT_7
, "timeout_7.ogg", GD_SP_CLASSIC
, 2, 26},
91 { GD_S_TIMEOUT_8
, "timeout_8.ogg", GD_SP_CLASSIC
, 2, 27},
92 { GD_S_TIMEOUT_9
, "timeout_9.ogg", GD_SP_CLASSIC
, 2, 28},
93 { GD_S_TIMEOUT
, "timeout.ogg", GD_SP_FORCE
, 2, 150, GD_S_NONE
},
94 { GD_S_EXPLOSION
, "explosion.ogg", GD_SP_CLASSIC
|GD_SP_FORCE
, 2, 100},
95 { GD_S_BOMB_EXPLOSION
, "bomb_explosion.ogg", GD_SP_FORCE
, 2, 100, GD_S_EXPLOSION
},
96 { GD_S_GHOST_EXPLOSION
, "ghost_explosion.ogg", GD_SP_FORCE
, 2, 100, GD_S_EXPLOSION
},
97 { GD_S_VOODOO_EXPLOSION
, "voodoo_explosion.ogg", GD_SP_FORCE
, 2, 100, GD_S_EXPLOSION
},
98 { GD_S_NITRO_EXPLOSION
, "nitro_explosion.ogg", GD_SP_FORCE
, 2, 100, GD_S_EXPLOSION
},
99 { GD_S_BOMB_PLACE
, "bomb_place.ogg", 0, 2, 10, GD_S_NONE
},
100 { GD_S_FINISHED
, "finished.ogg", GD_SP_CLASSIC
|GD_SP_FORCE
|GD_SP_LOOPED
, 2, 15}, /* precedence larger than normal, but smaller than timeout sounds */
101 { GD_S_SWITCH_BITER
, "switch_biter.ogg", 0, 2, 10, GD_S_NONE
},
102 { GD_S_SWITCH_CREATURES
, "switch_creatures.ogg", 0, 2, 10, GD_S_NONE
},
103 { GD_S_SWITCH_GRAVITY
, "switch_gravity.ogg", 0, 2, 10, GD_S_NONE
},
104 { GD_S_SWITCH_EXPANDING
, "switch_expanding.ogg", 0, 2, 10, GD_S_NONE
},
105 { GD_S_SWITCH_CONVEYOR
, "switch_conveyor.ogg", 0, 2, 10, GD_S_NONE
},
106 { GD_S_SWITCH_REPLICATOR
, "switch_replicator.ogg", 0, 2, 10, GD_S_NONE
},
108 /* channel 3 sounds. */
109 { GD_S_AMOEBA
, "amoeba.ogg", GD_SP_CLASSIC
|GD_SP_LOOPED
, 3, 30},
110 { GD_S_AMOEBA_MAGIC
, "amoeba_and_magic.ogg", GD_SP_CLASSIC
|GD_SP_LOOPED
, 3, 40},
111 { GD_S_MAGIC_WALL
, "magic_wall.ogg", GD_SP_CLASSIC
|GD_SP_LOOPED
, 3, 35},
112 { GD_S_COVER
, "cover.ogg", GD_SP_CLASSIC
|GD_SP_LOOPED
, 3, 100},
113 { GD_S_PNEUMATIC_HAMMER
, "pneumatic.ogg", GD_SP_CLASSIC
|GD_SP_LOOPED
, 3, 50},
114 { GD_S_WATER
, "water.ogg", GD_SP_LOOPED
, 3, 20, GD_S_NONE
},
115 { GD_S_CRACK
, "crack.ogg", GD_SP_CLASSIC
, 3, 150},
116 { GD_S_GRAVITY_CHANGE
, "gravity_change.ogg", 0, 3, 60, GD_S_NONE
},
119 /* the bonus life sound has nothing to do with the cave. */
120 /* playing on channel 4. */
121 { GD_S_BONUS_LIFE
, "bonus_life.ogg", 0, 4, 0, GD_S_NONE
},
133 gd_sound_get_filename(GdSound sound
)
135 g_assert(sound
>=GD_S_NONE
&& sound
<GD_S_MAX
);
136 return sound_flags
[sound
].filename
;
140 gd_sound_is_looped(GdSound sound
)
142 g_assert(sound
>=GD_S_NONE
&& sound
<GD_S_MAX
);
143 return (sound_flags
[sound
].flags
&GD_SP_LOOPED
)!=0;
147 gd_sound_is_fake(GdSound sound
)
149 g_assert(sound
>=GD_S_NONE
&& sound
<GD_S_MAX
);
150 return (sound_flags
[sound
].flags
&GD_SP_FAKE
)!=0;
154 gd_sound_is_classic(GdSound sound
)
156 g_assert(sound
>=GD_S_NONE
&& sound
<GD_S_MAX
);
157 return (sound_flags
[sound
].flags
&GD_SP_CLASSIC
)!=0;
161 gd_sound_force_start(GdSound sound
)
163 g_assert(sound
>=GD_S_NONE
&& sound
<GD_S_MAX
);
164 return (sound_flags
[sound
].flags
&GD_SP_FORCE
)!=0;
169 gd_sound_classic_equivalent(GdSound sound
)
171 g_assert(sound
>=GD_S_NONE
&& sound
<GD_S_MAX
);
172 if (gd_sound_is_classic(sound
))
176 return sound_flags
[sound
].replace
;
180 gd_sound_get_channel(GdSound sound
)
182 g_assert(sound
>=GD_S_NONE
&& sound
<GD_S_MAX
);
184 return sound_flags
[sound
].channel
;
188 gd_sound_get_precedence(GdSound sound
)
190 g_assert(sound
>=GD_S_NONE
&& sound
<GD_S_MAX
);
192 return sound_flags
[sound
].precedence
;
202 gd_cave_sound_db_init()
205 /* only if sound compiled in. otherwise does nothing. */
208 /* check if the number of sounds in the array matches. */
209 g_assert(G_N_ELEMENTS(sound_flags
)==GD_S_MAX
);
211 for (i
=0; i
<GD_S_MAX
; i
++) {
212 if (sound_flags
[i
].sound
!=i
) {
213 g_critical("sound db index mismatch: %d", i
);
214 g_assert_not_reached();
217 if (!gd_sound_is_classic(i
)) {
218 /* if it is a non classic sound */
219 if (!gd_sound_is_classic(gd_sound_classic_equivalent(i
))) {
220 g_critical("replacement for a non-classic sound must be classic: %d", i
);
221 g_assert_not_reached();
225 if (i
>GD_S_NONE
&& (gd_sound_get_channel(i
)<1 || gd_sound_get_channel(i
)>4)) {
226 g_critical("channel must be 1<=c<=4 for sound %d", i
);
227 g_assert_not_reached();
233 /* plays sound in a cave */
235 gd_sound_play(GdCave
*cave
, GdSound sound
)
240 if (sound
==GD_S_NONE
)
243 switch(gd_sound_get_channel(sound
)) {
244 case 1: s
=&cave
->sound1
; break;
245 case 2: s
=&cave
->sound2
; break;
246 case 3: s
=&cave
->sound3
; break;
248 g_assert_not_reached();
251 if (gd_sound_get_precedence(sound
)>=gd_sound_get_precedence(*s
)) {
252 // g_print("sound: preferred %s over %s\n", sound_flags[sound].filename, sound_flags[*s].filename);