1 /*****************************************************************************
2 * puzzle.c : Puzzle game
3 *****************************************************************************
4 * Copyright (C) 2005-2009 the VideoLAN team
7 * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_filter.h>
38 #include "filter_picture.h"
40 /*****************************************************************************
42 *****************************************************************************/
43 #define ROWS_TEXT N_("Number of puzzle rows")
44 #define ROWS_LONGTEXT N_("Number of puzzle rows")
45 #define COLS_TEXT N_("Number of puzzle columns")
46 #define COLS_LONGTEXT N_("Number of puzzle columns")
47 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
48 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
50 #define CFG_PREFIX "puzzle-"
52 static int Open ( vlc_object_t * );
53 static void Close( vlc_object_t * );
56 set_description( N_("Puzzle interactive game video filter") )
57 set_shortname( N_( "Puzzle" ))
58 set_capability( "video filter2", 0 )
59 set_category( CAT_VIDEO )
60 set_subcategory( SUBCAT_VIDEO_VFILTER )
62 add_integer_with_range( CFG_PREFIX "rows", 4, 2, 16, NULL,
63 ROWS_TEXT, ROWS_LONGTEXT, false )
64 add_integer_with_range( CFG_PREFIX "cols", 4, 2, 16, NULL,
65 COLS_TEXT, COLS_LONGTEXT, false )
66 add_bool( CFG_PREFIX "black-slot", false, NULL,
67 BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, false )
69 set_callbacks( Open, Close )
73 /*****************************************************************************
75 *****************************************************************************/
76 static const char *const ppsz_filter_options[] = {
77 "rows", "cols", "black-slot", NULL
80 static picture_t *Filter( filter_t *, picture_t * );
81 static int Mouse( filter_t *, vlc_mouse_t *, const vlc_mouse_t *, const vlc_mouse_t * );
83 static bool IsFinished( filter_sys_t * );
84 static void Shuffle( filter_sys_t * );
85 static int PuzzleCallback( vlc_object_t *, char const *,
86 vlc_value_t, vlc_value_t, void * );
109 #define SHUFFLE_WIDTH 81
110 #define SHUFFLE_HEIGHT 13
111 static const char *shuffle_button[] =
113 ".................................................................................",
114 ".............. ............................ ........ ...... ...............",
115 ".............. ........................... ......... ........ ...............",
116 ".............. ........................... ......... ........ ...............",
117 ".. ....... . ....... .... ...... ...... ...... ........ ...",
118 ". .... ...... ... ...... .... ....... ......... ........ ....... .. ..",
119 ". ........... .... ...... .... ....... ......... ........ ...... .... .",
120 ". ....... .... ...... .... ....... ......... ........ ...... .",
121 ".. ...... .... ...... .... ....... ......... ........ ...... .......",
122 "...... ...... .... ...... .... ....... ......... ........ ...... .......",
123 ". .... ...... .... ...... ... ....... ......... ........ ....... .... .",
124 ".. ....... .... ....... . ....... ......... ........ ........ ..",
125 "................................................................................."
132 static int Open( vlc_object_t *p_this )
134 filter_t *p_filter = (filter_t *)p_this;
138 if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) )
140 msg_Err( p_filter, "Input and output format does not match" );
144 /* Allocate structure */
145 p_filter->p_sys = p_sys = malloc( sizeof( *p_sys ) );
149 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
152 p_sys->pi_order = NULL;
154 vlc_mutex_init( &p_sys->lock );
155 p_sys->change.i_rows =
156 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "rows" );
157 p_sys->change.i_cols =
158 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "cols" );
159 p_sys->change.b_blackslot =
160 var_CreateGetBoolCommand( p_filter, CFG_PREFIX "black-slot" );
161 p_sys->b_change = true;
163 var_AddCallback( p_filter, CFG_PREFIX "rows", PuzzleCallback, p_sys );
164 var_AddCallback( p_filter, CFG_PREFIX "cols", PuzzleCallback, p_sys );
165 var_AddCallback( p_filter, CFG_PREFIX "black-slot", PuzzleCallback, p_sys );
167 p_filter->pf_video_filter = Filter;
168 p_filter->pf_video_mouse = Mouse;
176 static void Close( vlc_object_t *p_this )
178 filter_t *p_filter = (filter_t *)p_this;
179 filter_sys_t *p_sys = p_filter->p_sys;
181 var_DelCallback( p_filter, CFG_PREFIX "rows", PuzzleCallback, p_sys );
182 var_DelCallback( p_filter, CFG_PREFIX "cols", PuzzleCallback, p_sys );
183 var_DelCallback( p_filter, CFG_PREFIX "black-slot", PuzzleCallback, p_sys );
185 vlc_mutex_destroy( &p_sys->lock );
186 free( p_sys->pi_order );
194 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
196 filter_sys_t *p_sys = p_filter->p_sys;
198 picture_t *p_outpic = filter_NewPicture( p_filter );
201 picture_Release( p_pic );
206 vlc_mutex_lock( &p_sys->lock );
207 if( p_sys->b_change )
209 p_sys->i_rows = p_sys->change.i_rows;
210 p_sys->i_cols = p_sys->change.i_cols;
211 p_sys->b_blackslot = p_sys->change.b_blackslot;
212 p_sys->b_change = false;
216 vlc_mutex_unlock( &p_sys->lock );
219 const int i_rows = p_sys->i_rows;
220 const int i_cols = p_sys->i_cols;
222 /* Draw each piece of the puzzle at the right place */
223 for( int i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
225 const plane_t *p_in = &p_pic->p[i_plane];
226 plane_t *p_out = &p_outpic->p[i_plane];
228 for( int i = 0; i < i_cols * i_rows; i++ )
230 int i_piece_height = p_out->i_visible_lines / i_rows;
231 int i_piece_width = p_out->i_visible_pitch / i_cols;
233 int i_col = (i % i_cols) * i_piece_width;
234 int i_row = (i / i_cols) * i_piece_height;
235 int i_last_row = i_row + i_piece_height;
237 int i_ocol = (p_sys->pi_order[i] % i_cols) * i_piece_width;
238 int i_orow = (p_sys->pi_order[i] / i_cols) * i_piece_height;
240 if( p_sys->b_blackslot && !p_sys->b_finished && i == p_sys->i_selected )
242 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
243 for( int r = i_row; r < i_last_row; r++ )
245 memset( p_out->p_pixels + r * p_out->i_pitch + i_col,
246 color, i_piece_width );
251 for( int r = i_row, or = i_orow; r < i_last_row; r++, or++ )
253 memcpy( p_out->p_pixels + r * p_out->i_pitch + i_col,
254 p_in->p_pixels + or * p_in->i_pitch + i_ocol,
259 /* Draw the borders of the selected slot */
260 if( i_plane == 0 && !p_sys->b_blackslot && p_sys->i_selected == i )
262 memset( p_out->p_pixels + i_row * p_out->i_pitch + i_col,
263 0xff, i_piece_width );
264 for( int r = i_row; r < i_last_row; r++ )
266 p_out->p_pixels[r * p_out->i_pitch + i_col + 0 + 0 ] = 0xff;
267 p_out->p_pixels[r * p_out->i_pitch + i_col + i_piece_width - 1 ] = 0xff;
269 memset( p_out->p_pixels + (i_last_row - 1) * p_out->i_pitch + i_col,
270 0xff, i_piece_width );
275 /* Draw the 'Shuffle' button if the puzzle is finished */
276 if( p_sys->b_finished )
278 plane_t *p_out = &p_outpic->p[Y_PLANE];
279 for( int i = 0; i < SHUFFLE_HEIGHT; i++ )
281 for( int j = 0; j < SHUFFLE_WIDTH; j++ )
283 if( shuffle_button[i][j] == '.' )
284 p_out->p_pixels[ i * p_out->i_pitch + j ] = 0xff;
289 return CopyInfoAndRelease( p_outpic, p_pic );
292 static int Mouse( filter_t *p_filter, vlc_mouse_t *p_mouse,
293 const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
295 filter_sys_t *p_sys = p_filter->p_sys;
296 const video_format_t *p_fmt = &p_filter->fmt_in.video;
298 /* Only take events inside the puzzle erea */
299 if( p_new->i_x < 0 || p_new->i_x >= (int)p_fmt->i_width ||
300 p_new->i_y < 0 || p_new->i_y >= (int)p_fmt->i_height )
304 const bool b_clicked = vlc_mouse_HasPressed( p_old, p_new, MOUSE_BUTTON_LEFT );
306 /* If the puzzle is finished, shuffle it if needed */
307 if( p_sys->b_finished )
310 p_new->i_x < SHUFFLE_WIDTH && p_new->i_y < SHUFFLE_HEIGHT )
312 p_sys->b_change = true;
317 /* This is the only case where we can forward the mouse */
326 const int i_pos_x = p_new->i_x * p_sys->i_cols / p_fmt->i_width;
327 const int i_pos_y = p_new->i_y * p_sys->i_rows / p_fmt->i_height;
328 const int i_pos = i_pos_y * p_sys->i_cols + i_pos_x;
330 if( p_sys->i_selected == -1 )
332 p_sys->i_selected = i_pos;
334 else if( p_sys->i_selected == i_pos && !p_sys->b_blackslot )
336 p_sys->i_selected = -1;
338 else if( ( p_sys->i_selected == i_pos + 1 && p_sys->i_selected%p_sys->i_cols != 0 )
339 || ( p_sys->i_selected == i_pos - 1 && i_pos % p_sys->i_cols != 0 )
340 || p_sys->i_selected == i_pos + p_sys->i_cols
341 || p_sys->i_selected == i_pos - p_sys->i_cols )
343 /* Swap two pieces */
344 int a = p_sys->pi_order[ p_sys->i_selected ];
345 p_sys->pi_order[ p_sys->i_selected ] = p_sys->pi_order[ i_pos ];
346 p_sys->pi_order[ i_pos ] = a;
348 p_sys->i_selected = p_sys->b_blackslot ? i_pos : -1;
349 p_sys->b_finished = IsFinished( p_sys );
354 /*****************************************************************************
356 *****************************************************************************/
357 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
358 vlc_value_t oldval, vlc_value_t newval,
361 VLC_UNUSED(p_this); VLC_UNUSED(oldval);
362 filter_sys_t *p_sys = (filter_sys_t *)p_data;
364 vlc_mutex_lock( &p_sys->lock );
365 if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
367 p_sys->change.i_rows = __MAX( 1, newval.i_int );
369 else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
371 p_sys->change.i_cols = __MAX( 1, newval.i_int );
373 else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
375 p_sys->change.b_blackslot = newval.b_bool;
377 p_sys->b_change = true;
378 vlc_mutex_unlock( &p_sys->lock );
383 static bool IsFinished( filter_sys_t *p_sys )
385 for( int i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
387 if( i != p_sys->pi_order[i] )
393 static bool IsValid( filter_sys_t *p_sys )
395 const int i_count = p_sys->i_cols * p_sys->i_rows;
397 if( !p_sys->b_blackslot )
401 for( int i = 0; i < i_count; i++ )
403 if( p_sys->pi_order[i] == i_count - 1 )
405 d += i / p_sys->i_cols + 1;
408 for( int j = i+1; j < i_count; j++ )
410 if( p_sys->pi_order[j] == i_count - 1 )
412 if( p_sys->pi_order[i] > p_sys->pi_order[j] )
419 static void Shuffle( filter_sys_t *p_sys )
421 const unsigned i_count = p_sys->i_cols * p_sys->i_rows;
423 free( p_sys->pi_order );
425 p_sys->pi_order = calloc( i_count, sizeof(*p_sys->pi_order) );
428 for( unsigned i = 0; i < i_count; i++ )
429 p_sys->pi_order[i] = -1;
431 for( unsigned c = 0; c < i_count; )
433 unsigned i = ((unsigned)vlc_mrand48()) % i_count;
434 if( p_sys->pi_order[i] == -1 )
435 p_sys->pi_order[i] = c++;
437 p_sys->b_finished = IsFinished( p_sys );
439 } while( p_sys->b_finished || !IsValid( p_sys ) );
441 if( p_sys->b_blackslot )
443 for( unsigned i = 0; i < i_count; i++ )
445 if( p_sys->pi_order[i] == i_count - 1 )
447 p_sys->i_selected = i;
454 p_sys->i_selected = -1;