source: 2014/24/EemeliK/Zombieland/Jypeli/Assets/Explosion.cs @ 5974

Revision 5974, 10.2 KB checked in by empaheik, 4 years ago (diff)
Line 
1#region MIT License
2/*
3 * Copyright (c) 2009 University of Jyväskylä, Department of Mathematical
4 * Information Technology.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24#endregion
25
26/*
27 * Authors: Tero Jäntti, Tomi Karppinen, Janne Nikkanen.
28 */
29
30using AdvanceMath;
31using Microsoft.Xna.Framework;
32using Microsoft.Xna.Framework.Graphics;
33using System;
34using Physics2DDotNet.Shapes;
35using Microsoft.Xna.Framework.Audio;
36
37namespace Jypeli.Assets
38{
39    /// <summary>
40    /// Räjähdys.
41    /// </summary>
42    public class Explosion : GameObject
43    {
44        SynchronousList<PhysicsObject> shockwaveHitObjects = new SynchronousList<PhysicsObject>();
45        private static Image commonImage = null;
46        private static SoundEffect commonSound = null;
47
48        private GameObject shockWave;
49        private bool initialized;
50
51        /// <summary>
52        /// Onko paineaalto käytössä.
53        /// </summary>
54        public bool UseShockWave { get; set; }
55
56        /// <summary>
57        /// Suurin säde, johon räjähdys voi kasvaa.
58        /// </summary>
59        public double MaxRadius { get; set; }
60
61        /// <summary>
62        /// Räjähdyksen ääniefekti.
63        /// </summary>
64        public SoundEffect Sound { get; set; }
65
66        /// <summary>
67        /// Räjähdyksen nykyinen säde.
68        /// </summary>
69        public double CurrentRadius
70        {
71            get { return Size.X; }
72            private set
73            {
74                Size = new Vector( value, value );
75            }
76        }
77
78        /// <summary>
79        /// Paineaallon väri.
80        /// <example>
81        /// Shockwave.Color = Color.White
82        /// </example>
83        /// </summary>
84        public Color ShockwaveColor
85        {
86            get { return shockWave.Color; }
87            set { shockWave.Color = value; }
88        }
89
90        /// <summary>
91        /// Räjähdyksen leviämisnopeus (pikseliä sekunnissa)
92        /// </summary>
93        public double Speed { get; set; }
94
95        /// <summary>
96        /// Voima, jolla räjähdyksen paineaallon uloin reuna heittää olioita räjähdyksestä poispäin.
97        /// Vihje: voit käyttää myös negatiivisia arvoja, jolloin räjähdys imee olioita sisäänsä.
98        /// </summary>
99        public double Force { get; set; }
100
101        /// <summary>
102        /// Tapahtuu, kun paineaalto osuu peliolioon.
103        /// </summary>
104        public event Action<IPhysicsObject, Vector> ShockwaveReachesObject;
105
106        /// <summary>
107        /// Luo uuden räjähdyksen entisen pohjalta.
108        /// </summary>
109        /// <param name="src">Kopioitava räjähdys</param>
110        public Explosion( Explosion src )
111            : this( src.MaxRadius )
112        {
113            this.UseShockWave = src.UseShockWave;
114            this.ShockwaveColor = src.ShockwaveColor;
115            this.Speed = src.Speed;
116            this.Force = src.Force;
117        }
118
119        /// <summary>
120        /// Luo uuden räjähdyksen.
121        /// </summary>
122        /// <param name="radius">Räjähdyksen säde.</param>
123        public Explosion( double radius )
124            : base( 0.1, 0.1, Shape.Circle )
125        {
126            UseShockWave = true;
127            MaxRadius = radius;
128            Speed = 250.0;
129            Force = 1000.0;
130            shockWave = new GameObject( 1, 1, Shape.Circle );
131            shockWave.Color = new Color( 240, 248, 255, 60 );
132            Add( shockWave );
133
134            Game.AssertInitialized( PreloadContent );
135
136            initialized = false;
137            IsUpdated = true;
138        }
139
140        private void PreloadContent()
141        {
142            if ( commonImage == null ) commonImage = Game.LoadImageFromResources( "explosion" );
143            if ( commonSound == null ) commonSound = Game.LoadSoundEffectFromResources( "explosionSound" );
144
145            Image = commonImage;
146            Sound = commonSound;
147        }
148
149        private void OnShockwaveReachesObject( IPhysicsObject obj, Vector swForce )
150        {
151            if ( ShockwaveReachesObject != null )
152                ShockwaveReachesObject( obj, swForce );
153        }
154
155        /// <summary>
156        /// Laukaisee aliohjelman handler, kun tämän räjähdyksen paineaalto osuu olioon o.
157        /// </summary>
158        /// <param name="o">Olio, johon paineaallon on osuttava</param>
159        /// <param name="handler">Tapahtuman käsittelevä aliohjelma</param>
160        public void AddShockwaveHandler( IPhysicsObject o, Action<IPhysicsObject, Vector> handler )
161        {
162            if ( o == null ) throw new NullReferenceException( "Object must not be null" );
163            if ( handler == null ) throw new NullReferenceException( "Handler must not be null" );
164
165            this.ShockwaveReachesObject += delegate( IPhysicsObject target, Vector shockForce )
166            {
167                if ( target == o )
168                    handler( target, shockForce );
169            };
170        }
171
172        /// <summary>
173        /// Laukaisee aliohjelman handler, kun tämän räjähdyksen paineaalto osuu olioon o.
174        /// </summary>
175        /// <param name="o">Olio, johon paineaallon on osuttava</param>
176        /// <param name="handler">Tapahtuman käsittelevä aliohjelma</param>
177        public void AddShockwaveHandler(string tag, Action<IPhysicsObject, Vector> handler)
178        {
179            if (tag == null) throw new NullReferenceException("Tag must not be null");
180            if (handler == null) throw new NullReferenceException("Handler must not be null");
181
182            this.ShockwaveReachesObject += delegate(IPhysicsObject target, Vector shockForce)
183            {
184                if (target.Tag == tag)
185                    handler(target, shockForce);
186            };
187        }
188
189        private void applyShockwave( PhysicsObject target, Vector distance )
190        {
191            double distanceFromEdge = distance.Magnitude - CurrentRadius;
192            if ( distanceFromEdge >= 0 )
193                return;
194
195            double relDistance = ( CurrentRadius + distanceFromEdge ) / CurrentRadius;
196            double shockQuotient = 1 / Math.Pow( relDistance, 2 );
197            double shockForce = Force * shockQuotient;
198
199            if ( Math.Abs( shockForce ) > float.Epsilon )
200            {
201                Vector shockVector = Vector.FromLengthAndAngle( shockForce, distance.Angle );
202                target.Hit( shockVector );
203
204                if ( !shockwaveHitObjects.Contains( target ) )
205                {
206                    OnShockwaveReachesObject( target, shockVector );
207                    shockwaveHitObjects.Add( target );
208                }
209            }
210        }
211
212        /// <summary>
213        /// Ajetaan kun pelitilannetta päivitetään. Päivityksen voi toteuttaa omassa luokassa toteuttamalla tämän
214        /// metodin. Perityn luokan metodissa tulee kutsua kantaluokan metodia.
215        /// </summary>
216        public override void Update( Time time )
217        {
218            // this is done only once, after being added to the game.
219            if ( !initialized )
220            {
221                PlaySound();
222                shockwaveHitObjects.Clear();
223                initialized = true;
224            }
225
226            if ( CurrentRadius > MaxRadius )
227            {
228                this.Destroy();
229                return;
230            }
231
232            double dt = time.SinceLastUpdate.TotalSeconds;
233            CurrentRadius += dt * Speed;
234            shockwaveHitObjects.Update( time );
235
236            if ( UseShockWave )
237            {
238                if ( Force > 0 )
239                {
240                    shockWave.Size = new Vector( 2 * CurrentRadius, 2 * CurrentRadius );
241                }
242
243                foreach ( var layer in Jypeli.Game.Instance.Layers )
244                {
245                    foreach ( var o in layer.Objects )
246                    {
247                        if ( o is PhysicsObject )
248                        {
249                            PhysicsObject po = (PhysicsObject)o;
250
251                            if ( po.IgnoresExplosions )
252                            {
253                                // No shockwave
254                                continue;
255                            }
256
257                            // Shockwave
258                            Vector distance = o.AbsolutePosition - this.AbsolutePosition;
259                            applyShockwave( po, distance );
260                        }
261                    }
262                }
263            }
264
265            base.Update( time );
266        }
267
268        private void PlaySound()
269        {
270            if ( Sound == null )
271                return;
272
273            // play the sound
274            double pitch = RandomGen.NextDouble( -0.1, 0.1 ); // add some variation to explosion sound
275            double pan = this.Position.X / ( Game.Screen.Width / 2 ); // sound comes from left or right speaker
276            pan = AdvanceMath.MathHelper.Clamp( (float)pan, (float)-1.0, (float)1.0 );
277            if ( !double.IsNaN( pan ) )  // sometimes pan can be Nan, that is why this check is here
278            {
279                Sound.Play( 1.0f, pitch, pan );
280            }
281        }
282    }
283}
Note: See TracBrowser for help on using the repository browser.