Sfoglia il codice sorgente

AudioToy added to the Sandbox

As the name implies, this toy demonstrates using audio from script. It also showcases:
- reading in objects from TAML instead of creating them programatically
- behavior connections
Mike Lilligreen 12 anni fa
parent
commit
292f1ad173

+ 3 - 0
modules/AudioToy/1/assets/Pause.asset.taml

@@ -0,0 +1,3 @@
+<ImageAsset
+    AssetName="Pause"
+    ImageFile="Pause.png" />

BIN
modules/AudioToy/1/assets/Pause.png


+ 3 - 0
modules/AudioToy/1/assets/Play.asset.taml

@@ -0,0 +1,3 @@
+<ImageAsset
+    AssetName="Play"
+    ImageFile="Play.png" />

BIN
modules/AudioToy/1/assets/Play.png


+ 3 - 0
modules/AudioToy/1/assets/SoundOff.asset.taml

@@ -0,0 +1,3 @@
+<ImageAsset
+    AssetName="SoundOff"
+    ImageFile="SoundOff.png" />

BIN
modules/AudioToy/1/assets/SoundOff.png


+ 3 - 0
modules/AudioToy/1/assets/SoundOn.asset.taml

@@ -0,0 +1,3 @@
+<ImageAsset
+    AssetName="SoundOn"
+    ImageFile="SoundOn.png" />

BIN
modules/AudioToy/1/assets/SoundOn.png


+ 111 - 0
modules/AudioToy/1/module.cs

@@ -0,0 +1,111 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2014 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+function AudioToy::create(%this)
+{
+    // Load script files.
+    exec("./scripts/ButtonBehavior.cs");
+    exec("./scripts/AnimationBehavior.cs");
+    exec("./scripts/SoundBehavior.cs");
+    exec("./scripts/MusicBehavior.cs");
+    exec("./scripts/MuteBehavior.cs");
+
+    // Turn on input events for scene objects.
+    SandboxWindow.setUseObjectInputEvents(true);
+
+    // Set the camera.
+    SandboxWindow.setCameraSize(40, 30);
+    
+    // Setup default values.
+    AudioToy.Music = "ToyAssets:titleMusic";
+    
+    // Sandbox options
+    addSelectionOption("Title Music,Level Music,Win Music,Lose Music", "Background Music", 4, "setupMusic", true, "Choose a music file");
+    
+    // Reset the toy initially.
+    AudioToy.reset();
+}
+
+//-----------------------------------------------------------------------------
+
+function AudioToy::destroy(%this)
+{
+    // Stop all audio when quitting the module.
+    alxStopAll();
+}
+
+//-----------------------------------------------------------------------------
+
+function AudioToy::reset(%this)
+{
+    // Clear the scene.
+    SandboxScene.clear();
+    
+    // Stop all music.
+    alxStopAll();
+    
+    // Load scene objects from TAML.
+    %background = TamlRead("./objects/Background.taml");
+    %ground = TamlRead("./objects/Ground.taml");
+    %grass = TamlRead("./objects/Grass.taml");
+    %barbarian = TamlRead("./objects/Barbarian.taml");
+    %dwarf = TamlRead("./objects/Dwarf.taml");
+    %knight = TamlRead("./objects/Knight.taml");
+    %wizard = TamlRead("./objects/Wizard.taml");
+    %play = TamlRead("./objects/PlayButton.taml");
+    %mute = TamlRead("./objects/MuteButton.taml");
+
+    // Add objects to the Scene.
+    SandboxScene.add(%background);
+    SandboxScene.add(%ground);
+    SandboxScene.add(%grass);
+    SandboxScene.add(%barbarian);
+    SandboxScene.add(%dwarf);
+    SandboxScene.add(%knight);
+    SandboxScene.add(%wizard);
+    SandboxScene.add(%play);
+    SandboxScene.add(%mute);
+}
+
+//-----------------------------------------------------------------------------
+
+function AudioToy::setupMusic(%this, %value)
+{
+    // Reset the toy.
+    AudioToy.reset();
+
+    // Set the music to the proper audio asset.
+    switch$(%value)
+    {
+        case "Title Music":
+        AudioToy.Music = "ToyAssets:titleMusic";
+      
+        case "Level Music":
+        AudioToy.Music = "ToyAssets:level1Music";
+      
+        case "Win Music":
+        AudioToy.Music = "ToyAssets:winMusic";
+      
+        case "Lose Music":
+        AudioToy.Music = "ToyAssets:loseMusic";
+    }
+}

+ 15 - 0
modules/AudioToy/1/module.taml

@@ -0,0 +1,15 @@
+<ModuleDefinition
+    ModuleId="AudioToy"
+    VersionId="1"
+    Description="Plays background music and sound effects"
+    Dependencies="ToyAssets=1"
+    Type="toy"
+    ToyCategoryIndex="3"
+    ScriptFile="module.cs"
+    CreateFunction="create"
+    DestroyFunction="destroy">
+    <DeclaredAssets
+            Path="assets"
+            Extension="asset.taml"
+            Recurse="true"/>
+</ModuleDefinition>

+ 6 - 0
modules/AudioToy/1/objects/Background.taml

@@ -0,0 +1,6 @@
+<Sprite
+    SceneLayer="31"
+    Size="40 30"
+    BodyType="Static"
+    Image="@asset=ToyAssets:jungleSky"
+    Frame="0" />

+ 22 - 0
modules/AudioToy/1/objects/Barbarian.taml

@@ -0,0 +1,22 @@
+<Sprite
+    SceneLayer="5"
+    Size="4 4"
+    Position="-15 -6.9"
+    UseInputEvents="true"
+    Animation="ToyAssets:TD_Barbarian_WalkSouth" >
+    <Sprite.Behaviors>
+        <ButtonBehavior />
+        <AnimationBehavior 
+            DeathAnimation="ToyAssets:TD_Barbarian_Death" />
+        <SoundBehavior
+            DeathSound="ToyAssets:BarbarianDeathSound" />
+    </Sprite.Behaviors>
+    <Sprite.CollisionShapes>
+        <Polygon>
+            <Point>-2 -2</Point>
+            <Point>2 -2</Point>
+            <Point>2 2</Point>
+            <Point>-2 2</Point>
+        </Polygon>
+    </Sprite.CollisionShapes>
+</Sprite>

+ 22 - 0
modules/AudioToy/1/objects/Dwarf.taml

@@ -0,0 +1,22 @@
+<Sprite
+    SceneLayer="5"
+    Size="4 4"
+    Position="-5 -6.9"
+    UseInputEvents="true"
+    Animation="ToyAssets:TD_DwarfWalkSouth" >
+    <Sprite.Behaviors>
+        <ButtonBehavior />
+        <AnimationBehavior 
+            DeathAnimation="ToyAssets:TD_DwarfDeath" />
+        <SoundBehavior
+            DeathSound="ToyAssets:DwarfDeathSound" />
+    </Sprite.Behaviors>
+    <Sprite.CollisionShapes>
+        <Polygon>
+            <Point>-2 -2</Point>
+            <Point>2 -2</Point>
+            <Point>2 2</Point>
+            <Point>-2 2</Point>
+        </Polygon>
+    </Sprite.CollisionShapes>
+</Sprite>

+ 6 - 0
modules/AudioToy/1/objects/Grass.taml

@@ -0,0 +1,6 @@
+<Sprite
+    Size="40 2"
+    Position="0 -8.5"
+    BodyType="Static"
+    Image="@asset=ToyAssets:grassForeground"
+    Frame="0" />

+ 24 - 0
modules/AudioToy/1/objects/Ground.taml

@@ -0,0 +1,24 @@
+<Scroller
+    SceneGroup="10"
+    Size="40 6"
+    Position="0 -12"
+    FixedAngle="1"
+    BodyType="Static"
+    Image="@asset=ToyAssets:dirtGround"
+    Frame="0"
+    repeatX="0.666666687">
+    <Scroller.CollisionShapes>
+        <Edge>
+            <Point>-20 3</Point>
+            <Point>20 3</Point>
+        </Edge>
+        <Edge>
+            <Point>-20 3</Point>
+            <Point>-20 40</Point>
+        </Edge>
+        <Edge>
+            <Point>20 3</Point>
+            <Point>20 40</Point>
+        </Edge>
+    </Scroller.CollisionShapes>
+</Scroller>

+ 22 - 0
modules/AudioToy/1/objects/Knight.taml

@@ -0,0 +1,22 @@
+<Sprite
+    SceneLayer="5"
+    Size="4 4"
+    Position="15 -6.9"
+    UseInputEvents="true"
+    Animation="ToyAssets:TD_Knight_MoveSouth" >
+    <Sprite.Behaviors>
+        <ButtonBehavior />
+        <AnimationBehavior 
+            DeathAnimation="ToyAssets:TD_Knight_Death" />
+        <SoundBehavior
+            DeathSound="ToyAssets:KnightDeathSound" />
+    </Sprite.Behaviors>
+    <Sprite.CollisionShapes>
+        <Polygon>
+            <Point>-2 -2</Point>
+            <Point>2 -2</Point>
+            <Point>2 2</Point>
+            <Point>-2 2</Point>
+        </Polygon>
+    </Sprite.CollisionShapes>
+</Sprite>

+ 19 - 0
modules/AudioToy/1/objects/MuteButton.taml

@@ -0,0 +1,19 @@
+<Sprite
+    SceneLayer="1"
+    Size="2 2"
+    Position="17.5 13"
+    UseInputEvents="true"
+    Image="AudioToy:SoundOn" >
+    <Sprite.Behaviors>
+        <ButtonBehavior />
+        <MuteBehavior />
+    </Sprite.Behaviors>
+    <Sprite.CollisionShapes>
+        <Polygon>
+            <Point>-1 -1</Point>
+            <Point>1 -1</Point>
+            <Point>1 1</Point>
+            <Point>-1 1</Point>
+        </Polygon>
+    </Sprite.CollisionShapes>
+</Sprite>

+ 19 - 0
modules/AudioToy/1/objects/PlayButton.taml

@@ -0,0 +1,19 @@
+<Sprite
+    SceneLayer="1"
+    Size="2 2"
+    Position="14 13"
+    UseInputEvents="true"
+    Image="AudioToy:Play" >
+    <Sprite.Behaviors>
+        <ButtonBehavior />
+        <MusicBehavior />
+    </Sprite.Behaviors>
+    <Sprite.CollisionShapes>
+        <Polygon>
+            <Point>-1 -1</Point>
+            <Point>1 -1</Point>
+            <Point>1 1</Point>
+            <Point>-1 1</Point>
+        </Polygon>
+    </Sprite.CollisionShapes>
+</Sprite>

+ 22 - 0
modules/AudioToy/1/objects/Wizard.taml

@@ -0,0 +1,22 @@
+<Sprite
+    SceneLayer="5"
+    Size="4 4"
+    Position="5 -6.9"
+    UseInputEvents="true"
+    Animation="ToyAssets:TD_Wizard_WalkSouth" >
+    <Sprite.Behaviors>
+        <ButtonBehavior />
+        <AnimationBehavior 
+            DeathAnimation="ToyAssets:TD_Wizard_Death" />
+        <SoundBehavior
+            DeathSound="ToyAssets:WizardDeathSound" />
+    </Sprite.Behaviors>
+    <Sprite.CollisionShapes>
+        <Polygon>
+            <Point>-2 -2</Point>
+            <Point>2 -2</Point>
+            <Point>2 2</Point>
+            <Point>-2 2</Point>
+        </Polygon>
+    </Sprite.CollisionShapes>
+</Sprite>

+ 79 - 0
modules/AudioToy/1/scripts/AnimationBehavior.cs

@@ -0,0 +1,79 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2014 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+if (!isObject(AnimationBehavior))
+{
+    %template = new BehaviorTemplate(AnimationBehavior);
+
+    %template.addBehaviorField(DeathAnimation, "The animation asset Id", asset, "", AnimationAsset);
+
+    %template.addBehaviorInput(playDeath, "", "Plays the death animation");
+
+    %template.addBehaviorOutput(reset, "", "Called when an animation has reset");
+}
+
+//-----------------------------------------------------------------------------
+
+function AnimationBehavior::onAddToScene(%this, %scene)
+{
+    // Find out if this object has the button behavior.
+    %button = %this.owner.getBehavior("ButtonBehavior");
+
+    // If yes, create a behavior connection.
+    if (isObject(%button))
+        %this.owner.connect(%button, %this, buttonDown, playDeath);
+
+    // Save the starting animation state.
+    %this.StartAnimation = %this.owner.Animation;
+}
+
+//-----------------------------------------------------------------------------
+
+function AnimationBehavior::playDeath(%this, %fromBehavior, %fromOutput)
+{
+    // Change to the death animation if it's not already playing.
+    if (%this.owner.Animation $= %this.StartAnimation)
+        %this.owner.Animation = %this.DeathAnimation;
+}
+
+//-----------------------------------------------------------------------------
+
+function AnimationBehavior::onAnimationEnd(%this)
+{
+    // Get the current animation.
+    %animation = %this.owner.getAnimation();
+
+    // If it is the death animation, schedule a reset.
+    if (%animation $= %this.DeathAnimation)
+        %this.schedule(500, "resetAnimation");
+}
+
+//-----------------------------------------------------------------------------
+
+function AnimationBehavior::resetAnimation(%this)
+{
+    // Change the current animation to the initial state.
+    %this.owner.Animation = %this.StartAnimation;
+
+    // Raise a signal to other behaviors that the animation state has reset.
+    %this.owner.raise(%this, reset);
+}

+ 36 - 0
modules/AudioToy/1/scripts/ButtonBehavior.cs

@@ -0,0 +1,36 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2014 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+if (!isObject(ButtonBehavior))
+{
+    %template = new BehaviorTemplate(ButtonBehavior);
+
+    %template.addBehaviorOutput(buttonDown, "", "Called when the button is pushed down");
+}
+
+//-----------------------------------------------------------------------------
+
+function ButtonBehavior::onTouchDown(%this, %touchId, %worldPosition)
+{
+    // Raise a signal to other behaviors that something happened.
+    %this.owner.raise(%this, buttonDown);
+}

+ 80 - 0
modules/AudioToy/1/scripts/MusicBehavior.cs

@@ -0,0 +1,80 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2014 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+if (!isObject(MusicBehavior))
+{
+    %template = new BehaviorTemplate(MusicBehavior);
+
+    %template.addBehaviorInput(playMusic, "", "Plays the background music");
+}
+
+//-----------------------------------------------------------------------------
+
+function MusicBehavior::onAddToScene(%this, %scene)
+{
+    // Find out if this object has the button behavior.
+    %button = %this.owner.getBehavior("ButtonBehavior");
+
+    // If yes, create a behavior connection.
+    if (isObject(%button))
+        %this.owner.connect(%button, %this, buttonDown, playMusic);
+
+    // Initial value setup.
+    %this.owner.Music = "";
+    %this.owner.Paused = false;
+}
+
+//-----------------------------------------------------------------------------
+
+function MusicBehavior::playMusic(%this, %fromBehavior, %fromOutput)
+{
+    // Check which image is being used.
+    switch$ (%this.owner.Image)
+    {
+        case "AudioToy:Play":
+            // If there is no audio handle, start playing music.
+            if (%this.owner.Music $= "")
+                %this.owner.Music = alxPlay(AudioToy.Music);
+
+            // If the music is paused, unpause it.
+            if (%this.owner.Paused)
+            {
+                alxUnpause(%this.owner.Music);
+                %this.owner.Paused = false;
+            }
+                
+            // Change the image from play to pause.
+            %this.owner.Image = "AudioToy:Pause";
+
+        case "AudioToy:Pause":
+            // Only pause if we have a valid audio handle.
+            if (%this.owner.Music !$= "")
+            {
+                // Pause the music and set the paused flag to true.
+                alxPause(%this.owner.Music);
+                %this.owner.Paused = true;
+            }
+            
+            // Change the image from pause to play.
+            %this.owner.Image = "AudioToy:Play";
+    }
+}

+ 63 - 0
modules/AudioToy/1/scripts/MuteBehavior.cs

@@ -0,0 +1,63 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2014 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+if (!isObject(MuteBehavior))
+{
+    %template = new BehaviorTemplate(MuteBehavior);
+
+    %template.addBehaviorInput(muteAudio, "", "Puts all audio on mute");
+}
+
+//-----------------------------------------------------------------------------
+
+function MuteBehavior::onAddToScene(%this, %scene)
+{
+    // Find out if this object has the button behavior.
+    %button = %this.owner.getBehavior("ButtonBehavior");
+
+    // If yes, create a behavior connection.
+    if (isObject(%button))
+        %this.owner.connect(%button, %this, buttonDown, muteAudio);
+}
+
+//-----------------------------------------------------------------------------
+
+function MuteBehavior::muteAudio(%this, %fromBehavior, %fromOutput)
+{
+    // Check which image is being used.
+    switch$ (%this.owner.Image)
+    {
+        case "AudioToy:SoundOn":
+            // Set the volume on channel 0 to 0%.
+            alxSetChannelVolume(0, 0.0);
+                
+            // Change the image from unmute to mute.
+            %this.owner.Image = "AudioToy:SoundOff";
+
+        case "AudioToy:SoundOff":
+            // Set the volume on channel 0 to 100%.
+            alxSetChannelVolume(0, 1.0);
+
+            // Change the image from mute to unmute.
+            %this.owner.Image = "AudioToy:SoundOn";
+    }
+}

+ 77 - 0
modules/AudioToy/1/scripts/SoundBehavior.cs

@@ -0,0 +1,77 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2014 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+if (!isObject(SoundBehavior))
+{
+    %template = new BehaviorTemplate(SoundBehavior);
+
+    %template.addBehaviorField(DeathSound, "The audio asset Id", asset, "", AudioAsset);
+
+    %template.addBehaviorInput(playSound, "", "Plays the death sound");
+    %template.addBehaviorInput(removeBlock, "", "Removes a block preventing a sound from playing");
+}
+
+//-----------------------------------------------------------------------------
+
+function SoundBehavior::onAddToScene(%this, %scene)
+{
+    // Find out if this object has the button behavior.
+    %button = %this.owner.getBehavior("ButtonBehavior");
+
+    // If yes, create a behavior connection.
+    if (isObject(%button))
+        %this.owner.connect(%button, %this, buttonDown, playSound);
+
+    // Find out if this object has an animation behavior.
+    %animation = %this.owner.getBehavior("AnimationBehavior");
+
+    // If yes, create a behavior connection.
+    if (isObject(%animation))
+        %this.owner.connect(%animation, %this, reset, removeBlock);
+
+    // Set the sound block state to false.
+    %this.Block = false;
+}
+
+//-----------------------------------------------------------------------------
+
+function SoundBehavior::playSound(%this, %fromBehavior, %fromOutput)
+{
+    // Skip playing a sound if the object's animation state is not yet reset.
+    if (%this.Block)
+        return;
+
+    // Play a sound effect if nothing is currently playing.
+    if (!alxIsPlaying(%this.owner.Sound))
+        %this.owner.Sound = alxPlay(%this.DeathSound);
+
+    // Prevent the sound from playing again until a reset signal arrives.
+    %this.Block = true;
+}
+
+//-----------------------------------------------------------------------------
+
+function SoundBehavior::removeBlock(%this, %fromBehavior, %fromOutput)
+{
+    // Reset the sound block state.
+    %this.Block = false;
+}