~saurabhs/bomberman

ref: 7388608e07c64eb0449d1ca35c6ca676e9cf65ec bomberman/Assets/Scripts/Core/Bomb.cs -rw-r--r-- 10.1 KiB
7388608e — Saurabh S Minor code cleanup 4 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Bomberman
{
    public class Bomb : MonoBehaviour
    {
        #region globals
        /// <summary>
        /// the player who detonated the bomb
        /// </summary>
        public PlayerController parent;

        /// <summary>
        /// detonate time
        /// </summary>
        public float detonateTime = 2f;

        /// <summary>
        /// bomb range in grid
        /// </summary>
        public int range = 4;

        /// <summary>
        /// raw grid data
        /// </summary>
        private MapData mapData;

        /// <summary>
        /// list of wall gameobject
        /// </summary>
        private List<BlockMapper> wallBlocks;

        /// <summary>
        /// explsion placehodler,
        /// pink cube for now
        /// </summary>
        public GameObject explosionGO;

        /// <summary>
        /// objects created for explosion
        /// </summary>
        private List<GameObject> explosionsEffect = new List<GameObject>();

        /// <summary>
        /// list of powerup to spawn from
        /// </summary>
        public List<GameObject> powerups = new List<GameObject>();

        /// <summary>
        /// flag to spawn only one 
        /// powerup for each bomb
        /// </summary>
        private bool hasSpawnedAPowerup = false;

        /// <summary>
        /// 
        /// </summary>
        private Coroutine bombExplosionRoutine = null;

        #endregion

        #region unity lifecycle 
        private void Start()
        {
            if ( powerups == null || powerups.Count == 0 )
                throw new System.Exception( "Invalid powerups collection." );
        }
        #endregion

        #region trigger response
        private void OnTriggerEnter( Collider collider )
        {
            if ( collider.gameObject.layer == Constants.LAYER_EXPLOSION )
            {
                //remove layer to avoid generating multiple explosions
                gameObject.layer = Constants.LAYER_DEFAULT;
                collider.gameObject.layer = Constants.LAYER_DEFAULT;

                //is hit by explosion effect
                StopCoroutine( bombExplosionRoutine );
                StartCoroutine( Explosion( 0.1f ) );
            }
        }
        #endregion

        #region combat
        /// <summary>
        /// called from player after instantiation
        /// </summary>
        public Coroutine StartDetonation( PlayerController parent, MapData mapData, List<BlockMapper> wallBlocks, float rangeMultiplier = 1f, bool hasRemoteBomb = false )
        {
            this.parent = parent;
            this.mapData = mapData;
            this.wallBlocks = wallBlocks;
            range = ( int )(range * rangeMultiplier);

            //increase max time to detonate to 10s
            //incase the player doesn't activate manually
            if ( hasRemoteBomb )
                detonateTime = Constants.MAX_DETONATION_DELAY;

            bombExplosionRoutine = StartCoroutine( OnBombDetonate() );
            return bombExplosionRoutine;
        }

        public IEnumerator Explosion( float delay = 0.5f )
        {
            //create explosion at origin
            CreateExplosion( new Point( ( int )transform.position.x, ( int )transform.position.z ) );

            var currentGrid = new Point( Mathf.Abs( ( int )transform.position.x ), Mathf.Abs( ( int )transform.position.z ) );

            //check for grid extrems or if the next block is indestructible
            var canMoveUp = (currentGrid.y - 1) >= 0 && mapData.GetValue( currentGrid.x, currentGrid.y - 1 ) != Constants.INDESTRUCTABLE_WALL_ID;
            var canMoveDown = (currentGrid.y + 1) < mapData.height && mapData.GetValue( currentGrid.x, currentGrid.y + 1 ) != Constants.INDESTRUCTABLE_WALL_ID;
            var canMoveRight = (currentGrid.x + 1) < mapData.width && mapData.GetValue( currentGrid.x + 1, currentGrid.y ) != Constants.INDESTRUCTABLE_WALL_ID;
            var canMoveLeft = (currentGrid.x - 1) >= 0 && mapData.GetValue( currentGrid.x - 1, currentGrid.y ) != Constants.INDESTRUCTABLE_WALL_ID;

            //add grids to be used for explosin and update for next block
            for ( var i = 1; i <= range; i++ )
            {
                //up
                if ( canMoveUp )
                {
                    var point = new Point( ( int )transform.position.x, ( int )transform.position.z + i );

                    var isCurrentBlockDestructibleWall = mapData.GetValue( point.x, point.AbsY ) == Constants.DESTRUCTABLE_WALL_ID;
                    DestroyCurrentBlockAndUpdateMapData( isCurrentBlockDestructibleWall, point );

                    //check if next point is valid
                    canMoveUp = point.y + 1 <= 0 &&
                                !isCurrentBlockDestructibleWall &&
                                mapData.GetValue( point.x, point.AbsY - 1 ) != Constants.INDESTRUCTABLE_WALL_ID;
                }
                //down
                if ( canMoveDown )
                {
                    var point = new Point( ( int )transform.position.x, ( int )transform.position.z - i );

                    var isCurrentBlockDestructibleWall = mapData.GetValue( point.x, point.AbsY ) == Constants.DESTRUCTABLE_WALL_ID;
                    DestroyCurrentBlockAndUpdateMapData( isCurrentBlockDestructibleWall, point );


                    //check if next point is valid
                    canMoveDown = point.AbsY + 1 < mapData.height &&
                                    !isCurrentBlockDestructibleWall &&
                                    mapData.GetValue( point.x, point.AbsY + 1 ) != Constants.INDESTRUCTABLE_WALL_ID;
                }
                //right
                if ( canMoveRight )
                {
                    var point = new Point( ( int )transform.position.x + i, ( int )transform.position.z );

                    var isCurrentBlockDestructibleWall = mapData.GetValue( point.x, point.AbsY ) == Constants.DESTRUCTABLE_WALL_ID;
                    DestroyCurrentBlockAndUpdateMapData( isCurrentBlockDestructibleWall, point );

                    //check if next point is valid
                    canMoveRight = point.x + 1 < mapData.width &&
                                    !isCurrentBlockDestructibleWall &&
                                    mapData.GetValue( point.x + 1, point.AbsY ) != Constants.INDESTRUCTABLE_WALL_ID;
                }
                //left
                if ( canMoveLeft )
                {
                    var point = new Point( ( int )transform.position.x - i, ( int )transform.position.z );

                    var isCurrentBlockDestructibleWall = mapData.GetValue( point.x, point.AbsY ) == Constants.DESTRUCTABLE_WALL_ID;
                    DestroyCurrentBlockAndUpdateMapData( isCurrentBlockDestructibleWall, point );

                    //check if next point is valid
                    canMoveLeft = point.x - 1 >= 0 &&
                                !isCurrentBlockDestructibleWall &&
                                mapData.GetValue( point.x - 1, point.AbsY ) != Constants.INDESTRUCTABLE_WALL_ID;
                }

                yield return new WaitForSeconds( delay );
            }

            //post explosion cleanup
            //explosion over, clear the grid
            DestroyExplosionEffect();

            parent.OnBombDetonateEnd();
            Destroy( gameObject );
        }

        private void DestroyCurrentBlockAndUpdateMapData( bool isCurrentBlockDesWall, Point point )
        {
            //create new explosion
            CreateExplosion( point );

            if ( isCurrentBlockDesWall )
            {
                mapData.SetValue( point.x, point.AbsY, Constants.GROUND_ID );
                var index = point.x + (point.AbsY * mapData.width);

                var listIndex = wallBlocks.FindIndex( obj => obj.index == index );
                if ( listIndex != -1 )
                {
                    //destroy destructible wall
                    Destroy( wallBlocks[listIndex].tile.gameObject );
                    //remove from list
                    wallBlocks.RemoveAt( listIndex );

                    SpawnPowerup( point );
                }
            }
        }

        /// <summary>
        /// spawns powerup if none has
        /// spawn yet at 33% spawn rate
        /// </summary>
        private void SpawnPowerup( Point point )
        {
            //33% chances of a destructible wall spawning a powerup
            if ( Random.Range( 0, 100 ) % 3 == 0 && !hasSpawnedAPowerup )
            {
                hasSpawnedAPowerup = true;

                //get a random powerup
                var powerupGO = powerups[Random.Range( 0, powerups.Count )];

                //Instantiate and place at the broken wall
                var powerup = Instantiate( powerupGO, new Vector3( point.x, 0, point.y ), powerupGO.transform.rotation );
                powerup.name = $"{powerupGO.name}";

                //destroy if the powerup is not
                //picked up in the given time window
                Destroy( powerup, powerupGO.GetComponent<Powerup>().lifetime );
            }
        }

        /// <summary>
        /// instantiate explosion
        /// </summary>
        private void CreateExplosion( Point point )
        {
            var explosionEffect = Instantiate( explosionGO, new Vector3( point.x, 0, point.y ), Quaternion.identity );
            explosionEffect.name = "_explosion";
            explosionsEffect.Add( explosionEffect );
        }

        /// <summary>
        /// clear explosion objects
        /// </summary>
        private void DestroyExplosionEffect()
        {
            for ( int i = 0; i < explosionsEffect.Count; i++ )
            {
                Destroy( explosionsEffect[i].gameObject );
            }

            explosionsEffect.Clear();
        }

        /// <summary>
        /// Response on player detonate the bomb
        /// </summary>
        private IEnumerator OnBombDetonate()
        {
            parent.OnBombDetonateStart();

            yield return new WaitForSeconds( detonateTime );

            StartCoroutine( Explosion( 0.1f ) );
        }
        #endregion
    }
}