2009050
[gdash.git] / src / cavesound.c
blob1190b67a098ecda06914e27b99d1bae4907fe2f0
1 /*
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.
16 #include <glib.h>
17 #include "config.h"
18 #include "cave.h"
20 #ifdef GD_SOUND
21 typedef enum _sound_flag {
22 GD_SP_LOOPED = 1<<0,
23 GD_SP_CLASSIC = 1<<1,
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 */
26 } GdSoundFlag;
28 typedef struct _sound_property {
29 GdSound sound;
30 const char *filename;
31 int flags;
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 */
35 } SoundProperty;
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},
118 /* other sounds */
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},
123 #endif
129 some sound things
131 #ifdef GD_SOUND
132 const char *
133 gd_sound_get_filename(GdSound sound)
135 g_assert(sound>=GD_S_NONE && sound<GD_S_MAX);
136 return sound_flags[sound].filename;
139 gboolean
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;
146 gboolean
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;
153 gboolean
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;
160 gboolean
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;
168 gboolean
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))
173 return sound;
175 /* replacement */
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;
194 #endif
201 void
202 gd_cave_sound_db_init()
204 #ifdef GD_SOUND
205 /* only if sound compiled in. otherwise does nothing. */
206 int i;
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();
230 #endif
233 /* plays sound in a cave */
234 void
235 gd_sound_play(GdCave *cave, GdSound sound)
237 #ifdef GD_SOUND
238 GdSound *s;
240 if (sound==GD_S_NONE)
241 return;
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;
247 default:
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);
253 *s=sound;
255 #endif