source: 2013/30/MiskaK/MW2(My Warfare 2)/Paranneltu Jypeli/Levels/AbstractTileMap.cs @ 4507

Revision 4507, 16.7 KB checked in by anlakane, 6 years ago (diff)

Talletus.

Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5
6namespace Jypeli
7{
8    public abstract class AbstractTileMap<TileType>
9    {
10        public delegate void TileMethod( Vector position, double width, double height, IntPoint positionInLevelArray);
11        public delegate void TileMethod<T1>(Vector position, double width, double height, IntPoint positionInLevelArray, T1 p1);
12        public delegate void TileMethod<T1, T2>(Vector position, double width, double height, IntPoint positionInLevelArray, T1 p1, T2 p2);
13        public delegate void TileMethod<T1, T2, T3>(Vector position, double width, double height, IntPoint positionInLevelArray, T1 p1, T2 p2, T3 p3);
14        public delegate void TileMethod<T1, T2, T3, T4>(Vector position, double width, double height, IntPoint positionInLevelArray, T1 p1, T2 p2, T3 p3, T4 p4);
15        public delegate void TileMethod<T1, T2, T3, T4, T5>(Vector position, double width, double height, IntPoint positionInLevelArray, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5);
16        public delegate void TileMethod<T1, T2, T3, T4, T5, T6>(Vector position, double width, double height, IntPoint positionInLevelArray, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6);
17
18
19
20        struct TileMethodCall
21        {
22            public TileMethod method;
23            public int row;
24            public int col;
25
26            public TileMethodCall( TileMethod m, int row, int col )
27            {
28                this.method = m;
29                this.row = row;
30                this.col = col;
31            }
32
33            public void Invoke( double tileWidth, double tileHeight )
34            {
35                double realX = Game.Instance.Level.Left + ( col * tileWidth ) + ( tileWidth / 2 );
36                double realY = Game.Instance.Level.Top - ( row * tileHeight ) - ( tileHeight / 2 );
37                method( new Vector( realX, realY ), tileWidth, tileHeight, new IntPoint(row, col));
38            }
39        }
40
41        private List<TileMethodCall> optimized = new List<TileMethodCall>();
42        protected Dictionary<TileType, TileMethod> legend = new Dictionary<TileType, TileMethod>();
43        protected TileType[,] tiles;
44
45        protected abstract TileType Null { get; }
46
47        public AbstractTileMap(TileType[,] tiles)
48        {
49            if ( tiles.GetLength( 0 ) == 0 || tiles.GetLength( 1 ) == 0 )
50                throw new ArgumentException( "All dimensions of tiles must be at least 1" );
51
52            this.tiles = tiles;
53        }
54
55        /// <summary>
56        /// Rivien määrä kentässä (pystysuoraan).
57        /// </summary>
58        public int RowCount
59        {
60            get { return tiles.GetLength( 0 ); }
61        }
62
63        /// <summary>
64        /// Sarakkeiden määrä kentässä (vaakasuoraan).
65        /// </summary>
66        public int ColumnCount
67        {
68            get { return tiles.GetLength( 1 ); }
69        }
70
71        /// <summary>
72        /// Määrittää, että tietyn ruutukentän symbolin (<c>tileSymbol</c>) kohdalla
73        /// kutsutaan aliohjelmaa <c>f</c>. Huom! Käytä tämän aliohjelman kanssa metodia
74        /// Execute.
75        /// </summary>
76        /// <param name="tileSymbol">Merkki</param>
77        /// <param name="f">Aliohjelma</param>
78        public void SetTileMethod( TileType tileSymbol, TileMethod f )
79        {
80            legend[tileSymbol] = f;
81        }
82
83        /// <summary>
84        /// Määrittää, että tietyn ruutukentän symbolin (<c>tileSymbol</c>) kohdalla
85        /// kutsutaan aliohjelmaa <c>f</c>. Huom! Käytä tämän aliohjelman kanssa metodia
86        /// Execute.
87        /// </summary>
88        /// <typeparam name="T1">Parametrin tyyppi</typeparam>
89        /// <param name="tileSymbol">Merkki</param>
90        /// <param name="f">Aliohjelma</param>
91        /// <param name="p1">Parametri</param>
92        public void SetTileMethod<T1>( TileType tileSymbol, TileMethod<T1> f, T1 p1 )
93        {
94            legend[tileSymbol] = delegate( Vector p, double w, double h, IntPoint posInLevel ) { f( p, w, h, posInLevel, p1 ); };
95        }
96
97        /// <summary>
98        /// Määrittää, että tietyn ruutukentän symbolin (<c>tileSymbol</c>) kohdalla
99        /// kutsutaan aliohjelmaa <c>f</c>. Huom! Käytä tämän aliohjelman kanssa metodia
100        /// Execute.
101        /// </summary>
102        /// <typeparam name="T1">Ensimmäisen parametrin tyyppi</typeparam>
103        /// <typeparam name="T2">Toisen parametrin tyyppi</typeparam>
104        /// <param name="tileSymbol">Merkki</param>
105        /// <param name="f">Aliohjelma</param>
106        /// <param name="p1">Ensimmäinen parametri</param>
107        /// <param name="p2">Toinen parametri</param>
108        public void SetTileMethod<T1, T2>( TileType tileSymbol, TileMethod<T1, T2> f, T1 p1, T2 p2 )
109        {
110            legend[tileSymbol] = delegate(Vector p, double w, double h, IntPoint posInLevel) { f(p, w, h, posInLevel, p1, p2); };
111        }
112
113        /// <summary>
114        /// Määrittää, että tietyn ruutukentän symbolin (<c>tileSymbol</c>) kohdalla
115        /// kutsutaan aliohjelmaa <c>f</c>. Huom! Käytä tämän aliohjelman kanssa metodia
116        /// Execute.
117        /// </summary>
118        /// <typeparam name="T1">Ensimmäisen parametrin tyyppi</typeparam>
119        /// <typeparam name="T2">Toisen parametrin tyyppi</typeparam>
120        /// <param name="tileSymbol">Merkki</param>
121        /// <param name="f">Aliohjelma</param>
122        /// <param name="p1">Ensimmäinen parametri</param>
123        /// <param name="p2">Toinen parametri</param>
124        public void SetTileMethod<T1, T2, T3>( TileType tileSymbol, TileMethod<T1, T2, T3> f, T1 p1, T2 p2, T3 p3 )
125        {
126            legend[tileSymbol] = delegate(Vector p, double w, double h, IntPoint posInLevel) { f(p, w, h, posInLevel, p1, p2, p3); };
127        }
128
129        /// <summary>
130        /// Määrittää, että tietyn ruutukentän symbolin (<c>tileSymbol</c>) kohdalla
131        /// kutsutaan aliohjelmaa <c>f</c>. Huom! Käytä tämän aliohjelman kanssa metodia
132        /// Execute.
133        /// </summary>
134        /// <typeparam name="T1">Ensimmäisen parametrin tyyppi</typeparam>
135        /// <typeparam name="T2">Toisen parametrin tyyppi</typeparam>
136        /// <param name="tileSymbol">Merkki</param>
137        /// <param name="f">Aliohjelma</param>
138        /// <param name="p1">Ensimmäinen parametri</param>
139        /// <param name="p2">Toinen parametri</param>
140        public void SetTileMethod<T1, T2, T3, T4>(TileType tileSymbol, TileMethod<T1, T2, T3, T4> f, T1 p1, T2 p2, T3 p3, T4 p4)
141        {
142            legend[tileSymbol] = delegate(Vector p, double w, double h, IntPoint posInLevel) { f(p, w, h, posInLevel, p1, p2, p3, p4); };
143        }
144
145        /// <summary>
146        /// Määrittää, että tietyn ruutukentän symbolin (<c>tileSymbol</c>) kohdalla
147        /// kutsutaan aliohjelmaa <c>f</c>. Huom! Käytä tämän aliohjelman kanssa metodia
148        /// Execute.
149        /// </summary>
150        /// <typeparam name="T1">Ensimmäisen parametrin tyyppi</typeparam>
151        /// <typeparam name="T2">Toisen parametrin tyyppi</typeparam>
152        /// <param name="tileSymbol">Merkki</param>
153        /// <param name="f">Aliohjelma</param>
154        /// <param name="p1">Ensimmäinen parametri</param>
155        /// <param name="p2">Toinen parametri</param>
156        public void SetTileMethod<T1, T2, T3, T4, T5>(TileType tileSymbol, TileMethod<T1, T2, T3, T4, T5> f, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
157        {
158            legend[tileSymbol] = delegate(Vector p, double w, double h, IntPoint posInLevel) { f(p, w, h, posInLevel, p1, p2, p3, p4, p5); };
159        }
160
161        /// <summary>
162        /// Määrittää, että tietyn ruutukentän symbolin (<c>tileSymbol</c>) kohdalla
163        /// kutsutaan aliohjelmaa <c>f</c>. Huom! Käytä tämän aliohjelman kanssa metodia
164        /// Execute.
165        /// </summary>
166        /// <typeparam name="T1">Ensimmäisen parametrin tyyppi</typeparam>
167        /// <typeparam name="T2">Toisen parametrin tyyppi</typeparam>
168        /// <param name="tileSymbol">Merkki</param>
169        /// <param name="f">Aliohjelma</param>
170        /// <param name="p1">Ensimmäinen parametri</param>
171        /// <param name="p2">Toinen parametri</param>
172        public void SetTileMethod<T1, T2, T3, T4, T5, T6>(TileType tileSymbol, TileMethod<T1, T2, T3, T4, T5, T6> f, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6)
173        {
174            legend[tileSymbol] = delegate(Vector p, double w, double h, IntPoint posInLevel) { f(p, w, h, posInLevel, p1, p2, p3, p4, p5, p6); };
175        }
176
177
178
179        /// <summary>
180        /// Käy kentän kaikki merkit läpi ja kutsuu <c>SetTileMethod</c>-metodilla annettuja
181        /// aliohjelmia kunkin merkin kohdalla.
182        /// </summary>
183        /// <remarks>
184        /// Aliohjelmassa voi esimerkiksi luoda olion ruudun kohdalle.
185        /// </remarks>
186        public void Execute()
187        {
188            double h = Game.Instance.Level.Height / tiles.GetLength( 0 );
189            double w = Game.Instance.Level.Width / tiles.GetLength( 1 );
190            Execute( w, h );
191        }
192
193        /// <summary>
194        /// Käy kentän kaikki merkit läpi ja kutsuu <c>SetTileMethod</c>-metodilla annettuja
195        /// aliohjelmia kunkin merkin kohdalla.
196        /// </summary>
197        /// <remarks>
198        /// Aliohjelmassa voi esimerkiksi luoda olion ruudun kohdalle.
199        /// </remarks>
200        /// <param name="tileWidth">Yhden ruudun leveys.</param>
201        /// <param name="tileHeight">Yhden ruudun korkeus.</param>
202        public void Execute( double tileWidth, double tileHeight )
203        {
204            Game game = Game.Instance;
205            int width = tiles.GetLength( 1 );
206            int height = tiles.GetLength( 0 );
207
208            game.Level.Width = width * tileWidth;
209            game.Level.Height = height * tileHeight;
210
211            foreach ( var m in optimized )
212            {
213                m.Invoke( tileWidth, tileHeight );
214            }
215
216            for ( int y = height - 1; y >= 0; y-- )
217            {
218                for ( int x = 0; x < width; x++ )
219                {
220                    TileMethod method = GetMethodForSymbol( tiles[y, x] );
221
222                    if ( method != null )
223                    {
224                        double realX = game.Level.Left + ( x * tileWidth ) + ( tileWidth / 2 );
225                        double realY = game.Level.Top - ( y * tileHeight ) - ( tileHeight / 2 );
226                        method( new Vector( realX, realY ), tileWidth, tileHeight, new IntPoint(x, y));
227                    }
228                }
229            }
230        }
231
232        protected TileMethod GetMethodForSymbol( TileType symbol )
233        {
234            if ( symbol.Equals(Null) ) return null;
235
236            foreach ( var key in legend.Keys )
237            {
238                if ( SymbolEquals( key, symbol ) )
239                    return legend[key];
240            }
241
242            return null;
243        }
244
245        protected virtual bool SymbolEquals( TileType a, TileType b )
246        {
247            return a.Equals( b );
248        }
249
250        /// <summary>
251        /// Optimoi annetut ruudut niin, että useammat vierekkäiset oliot yhdistetään
252        /// isommiksi olioiksi. Älä käytä esim. kerättäville esineille.
253        /// </summary>
254        /// <param name="symbols">Optimoitavat symbolit</param>
255        public void Optimize( params TileType[] symbols )
256        {
257            for ( int i = 0; i < symbols.Length; i++ )
258                Optimize( symbols[i] );
259        }
260
261        /// <summary>
262        /// Optimoi annetut ruudut niin, että useammat vierekkäiset oliot yhdistetään
263        /// isommiksi olioiksi. Älä käytä esim. kerättäville esineille.
264        /// </summary>
265        /// <param name="sym">Optimoitava symboli</param>
266        public void Optimize( TileType sym )
267        {
268            TileMethod method = GetMethodForSymbol(sym);
269
270            if (method == null)
271                throw new ArgumentException("Symbol " + sym + " not added, cannot optimize.");
272
273            for ( int row = 0; row < RowCount; row++ )
274            {
275                for ( int col = 0; col < ColumnCount; col++ )
276                {
277                    int w, h;
278                    GetGreatestMatchingRectangle( sym, row, col, out w, out h );
279
280                    if ( w > 0 && h > 0 )
281                    {
282                        TileMethod newMethod = delegate (Vector oldPos, double oldWidth, double oldHeight, IntPoint oldPosInLevel)
283                        {
284                            Vector oldAdjust = new Vector(-oldWidth, oldHeight);
285                            Vector newAdjust = new Vector(oldWidth * w, -oldHeight * h);
286                            Vector newPos = oldPos + ( oldAdjust + newAdjust ) / 2;
287                            method( newPos, oldWidth * w, oldHeight * h, new IntPoint(row, col));
288                        };
289                        optimized.Add( new TileMethodCall( newMethod, row, col ) );
290                        SetArea( row, col, w, h, Null );
291                        col += w;
292                    }
293                }
294            }
295        }
296
297        private void GetGreatestMatchingRectangle( TileType sym, int row, int col, out int width, out int height )
298        {
299            for ( width = 0; width < ColumnCount - col; width++ )
300            {
301                if ( !SymbolEquals( tiles[row, col + width], sym ) )
302                    break;
303            }
304
305            if ( width == 0 )
306            {
307                height = 0;
308                return;
309            }
310
311            for ( height = 1; height < RowCount - row; height++ )
312            {
313                if ( !RowEquals( row + height, col, width, sym ) )
314                    break;
315            }
316        }
317
318        private bool RowEquals( int row, int col, int length, TileType sym )
319        {
320            for ( int i = col; i < col + length; i++ )
321            {
322                if ( !SymbolEquals( tiles[row, i], sym ) ) return false;
323            }
324
325            return true;
326        }
327
328        private void SetArea( int row, int col, int width, int height, TileType sym )
329        {
330            for ( int j = row; j < row + height; j++ )
331            {
332                for ( int i = col; i < col + width; i++ )
333                {
334                    tiles[j, i] = sym;
335                }
336            }
337        }
338
339        /// <summary>
340        /// Palauttaa annetun dimension pituuden (merkkeinä, ei pikseleinä).
341        /// </summary>
342        /// <param name="dimension">Dimensio. 0 antaa kentän korkeuden, 1 leveyden.</param>
343        /// <returns>Annetun dimension koko</returns>
344        public int GetLength( int dimension )
345        {
346            return tiles.GetLength( dimension );
347        }
348
349        /// <summary>
350        /// Palauttaa ruudussa olevan symbolin.
351        /// </summary>
352        /// <param name="row">Rivi</param>
353        /// <param name="col">Sarake</param>
354        /// <returns>symbolin</returns>
355        public TileType GetTile( int row, int col )
356        {
357            return tiles[row,col];
358        }
359
360        /// <summary>
361        /// Asettaa ruudussa olevan symbolin.
362        /// </summary>
363        /// <param name="row">Rivi</param>
364        /// <param name="col">Sarake</param>
365        /// <param name="c">Uusi merkki</param>
366        /// <returns>symbolin</returns>
367        public void SetTile( int row, int col, TileType c )
368        {
369            tiles[row,col] = c;
370        }
371
372        /// <summary>
373        /// Muuttaa luontialiohjelman tekemän olion kokoa.
374        /// </summary>
375        /// <param name="m">Luontialiohjelma</param>
376        /// <param name="newWidth">Uusi leveys oliolle</param>
377        /// <param name="newHeight">Uusi korkeus oliolle</param>
378        /// <returns></returns>
379        public static TileMethod ChangeSize( TileMethod m, double newWidth, double newHeight )
380        {
381            return new TileMethod( delegate( Vector p, double w, double h, IntPoint posInLevel ) { m( p, newWidth, newHeight, posInLevel); } );
382        }
383
384        /// <summary>
385        /// Muuttaa luontialiohjelman tekemän olion kokoa tietyllä kertoimilla.
386        /// </summary>
387        /// <param name="m">Luontialiohjelma</param>
388        /// <param name="widthMultiplier">Kerroin alkuperäiselle leveydelle</param>
389        /// <param name="heightMultiplier">Kerroin alkuperäiselle korkeudelle</param>
390        /// <returns></returns>
391        public static TileMethod ChangeSizeMultiplier( TileMethod m, double widthMultiplier, double heightMultiplier )
392        {
393            return new TileMethod( delegate( Vector p, double w, double h, IntPoint posInLevel) { m( p, w * widthMultiplier, h * heightMultiplier, posInLevel ); } );
394        }
395    }
396}
Note: See TracBrowser for help on using the repository browser.