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 const int i_pitch = p_in->i_pitch;
227 plane_t *p_out = &p_outpic->p[i_plane];
229 for( int i = 0; i < i_cols * i_rows; i++ )
231 int i_col = i % i_cols;
232 int i_row = i / i_cols;
233 int i_ocol = p_sys->pi_order[i] % i_cols;
234 int i_orow = p_sys->pi_order[i] / i_cols;
235 int i_last_row = i_row + 1;
236 i_orow *= p_in->i_lines / i_rows;
237 i_row *= p_in->i_lines / i_rows;
238 i_last_row *= p_in->i_lines / i_rows;
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( ; i_row < i_last_row; i_row++, i_orow++ )
245 memset( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
246 color, i_pitch / i_cols );
251 for( ; i_row < i_last_row; i_row++, i_orow++ )
253 memcpy( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
254 p_in->p_pixels + i_orow * i_pitch + i_ocol * i_pitch / i_cols,
261 /* Draw the borders of the selected slot */
262 if( p_sys->i_selected != -1 && !p_sys->b_blackslot )
264 const plane_t *p_in = &p_pic->p[Y_PLANE];
265 const int i_pitch = p_in->i_pitch;
266 plane_t *p_out = &p_outpic->p[Y_PLANE];
268 int i_col = p_sys->i_selected % i_cols;
269 int i_row = p_sys->i_selected / i_cols;
270 int i_last_row = i_row + 1;
271 i_row *= p_in->i_lines / i_rows;
272 i_last_row *= p_in->i_lines / i_rows;
273 memset( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
274 0xff, i_pitch / i_cols );
275 for( ; i_row < i_last_row; i_row++ )
277 p_out->p_pixels[ i_row * i_pitch
278 + i_col * i_pitch / i_cols ] = 0xff;
279 p_out->p_pixels[ i_row * i_pitch
280 + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff;
283 memset( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
284 0xff, i_pitch / i_cols );
287 /* Draw the 'Shuffle' button if the puzzle is finished */
288 if( p_sys->b_finished )
290 plane_t *p_out = &p_outpic->p[Y_PLANE];
291 for( int i = 0; i < SHUFFLE_HEIGHT; i++ )
293 for( int j = 0; j < SHUFFLE_WIDTH; j++ )
295 if( shuffle_button[i][j] == '.' )
296 p_out->p_pixels[ i * p_out->i_pitch + j ] = 0xff;
301 return CopyInfoAndRelease( p_outpic, p_pic );
304 static int Mouse( filter_t *p_filter, vlc_mouse_t *p_mouse,
305 const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
307 filter_sys_t *p_sys = p_filter->p_sys;
308 const video_format_t *p_fmt = &p_filter->fmt_in.video;
310 /* Only take events inside the puzzle erea */
311 if( p_new->i_x < 0 || p_new->i_x >= (int)p_fmt->i_width ||
312 p_new->i_y < 0 || p_new->i_y >= (int)p_fmt->i_height )
316 const bool b_clicked = vlc_mouse_HasPressed( p_old, p_new, MOUSE_BUTTON_LEFT );
318 /* If the puzzle is finished, shuffle it if needed */
319 if( p_sys->b_finished )
322 p_new->i_x < SHUFFLE_WIDTH && p_new->i_y < SHUFFLE_HEIGHT )
324 p_sys->b_change = true;
329 /* This is the only case where we can forward the mouse */
338 const int i_pos_x = p_new->i_x * p_sys->i_cols / p_fmt->i_width;
339 const int i_pos_y = p_new->i_y * p_sys->i_rows / p_fmt->i_height;
340 const int i_pos = i_pos_y * p_sys->i_cols + i_pos_x;
342 if( p_sys->i_selected == -1 )
344 p_sys->i_selected = i_pos;
346 else if( p_sys->i_selected == i_pos && !p_sys->b_blackslot )
348 p_sys->i_selected = -1;
350 else if( ( p_sys->i_selected == i_pos + 1 && p_sys->i_selected%p_sys->i_cols != 0 )
351 || ( p_sys->i_selected == i_pos - 1 && i_pos % p_sys->i_cols != 0 )
352 || p_sys->i_selected == i_pos + p_sys->i_cols
353 || p_sys->i_selected == i_pos - p_sys->i_cols )
355 /* Swap two pieces */
356 int a = p_sys->pi_order[ p_sys->i_selected ];
357 p_sys->pi_order[ p_sys->i_selected ] = p_sys->pi_order[ i_pos ];
358 p_sys->pi_order[ i_pos ] = a;
360 p_sys->i_selected = p_sys->b_blackslot ? i_pos : -1;
361 p_sys->b_finished = IsFinished( p_sys );
366 /*****************************************************************************
368 *****************************************************************************/
369 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
370 vlc_value_t oldval, vlc_value_t newval,
373 VLC_UNUSED(p_this); VLC_UNUSED(oldval);
374 filter_sys_t *p_sys = (filter_sys_t *)p_data;
376 vlc_mutex_lock( &p_sys->lock );
377 if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
379 p_sys->change.i_rows = __MAX( 1, newval.i_int );
381 else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
383 p_sys->change.i_cols = __MAX( 1, newval.i_int );
385 else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
387 p_sys->change.b_blackslot = newval.b_bool;
389 p_sys->b_change = true;
390 vlc_mutex_unlock( &p_sys->lock );
395 static bool IsFinished( filter_sys_t *p_sys )
397 for( int i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
399 if( i != p_sys->pi_order[i] )
405 static bool IsValid( filter_sys_t *p_sys )
407 const int i_count = p_sys->i_cols * p_sys->i_rows;
409 if( !p_sys->b_blackslot )
413 for( int i = 0; i < i_count; i++ )
415 if( p_sys->pi_order[i] == i_count - 1 )
417 d += i / p_sys->i_cols + 1;
420 for( int j = i+1; j < i_count; j++ )
422 if( p_sys->pi_order[j] == i_count - 1 )
424 if( p_sys->pi_order[i] > p_sys->pi_order[j] )
431 static void Shuffle( filter_sys_t *p_sys )
433 const unsigned i_count = p_sys->i_cols * p_sys->i_rows;
435 free( p_sys->pi_order );
437 p_sys->pi_order = calloc( i_count, sizeof(*p_sys->pi_order) );
440 for( unsigned i = 0; i < i_count; i++ )
441 p_sys->pi_order[i] = -1;
443 for( unsigned c = 0; c < i_count; )
445 unsigned i = ((unsigned)vlc_mrand48()) % i_count;
446 if( p_sys->pi_order[i] == -1 )
447 p_sys->pi_order[i] = c++;
449 p_sys->b_finished = IsFinished( p_sys );
451 } while( p_sys->b_finished || !IsValid( p_sys ) );
453 if( p_sys->b_blackslot )
455 for( unsigned i = 0; i < i_count; i++ )
457 if( p_sys->pi_order[i] == i_count - 1 )
459 p_sys->i_selected = i;
466 p_sys->i_selected = -1;