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>
37 #include "filter_picture.h"
39 /*****************************************************************************
41 *****************************************************************************/
42 #define ROWS_TEXT N_("Number of puzzle rows")
43 #define ROWS_LONGTEXT N_("Number of puzzle rows")
44 #define COLS_TEXT N_("Number of puzzle columns")
45 #define COLS_LONGTEXT N_("Number of puzzle columns")
46 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
47 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
49 #define CFG_PREFIX "puzzle-"
51 static int Open ( vlc_object_t
* );
52 static void Close( vlc_object_t
* );
55 set_description( N_("Puzzle interactive game video filter") )
56 set_shortname( N_( "Puzzle" ))
57 set_capability( "video filter2", 0 )
58 set_category( CAT_VIDEO
)
59 set_subcategory( SUBCAT_VIDEO_VFILTER
)
61 add_integer_with_range( CFG_PREFIX
"rows", 4, 2, 16, NULL
,
62 ROWS_TEXT
, ROWS_LONGTEXT
, false )
63 add_integer_with_range( CFG_PREFIX
"cols", 4, 2, 16, NULL
,
64 COLS_TEXT
, COLS_LONGTEXT
, false )
65 add_bool( CFG_PREFIX
"black-slot", false, NULL
,
66 BLACKSLOT_TEXT
, BLACKSLOT_LONGTEXT
, false )
68 set_callbacks( Open
, Close
)
72 /*****************************************************************************
74 *****************************************************************************/
75 static const char *const ppsz_filter_options
[] = {
76 "rows", "cols", "black-slot", NULL
79 static picture_t
*Filter( filter_t
*, picture_t
* );
80 static int Mouse( filter_t
*, vlc_mouse_t
*, const vlc_mouse_t
*, const vlc_mouse_t
* );
82 static bool IsFinished( filter_sys_t
* );
83 static void Shuffle( filter_sys_t
* );
84 static int PuzzleCallback( vlc_object_t
*, char const *,
85 vlc_value_t
, vlc_value_t
, void * );
108 #define SHUFFLE_WIDTH 81
109 #define SHUFFLE_HEIGHT 13
110 static const char *shuffle_button
[] =
112 ".................................................................................",
113 ".............. ............................ ........ ...... ...............",
114 ".............. ........................... ......... ........ ...............",
115 ".............. ........................... ......... ........ ...............",
116 ".. ....... . ....... .... ...... ...... ...... ........ ...",
117 ". .... ...... ... ...... .... ....... ......... ........ ....... .. ..",
118 ". ........... .... ...... .... ....... ......... ........ ...... .... .",
119 ". ....... .... ...... .... ....... ......... ........ ...... .",
120 ".. ...... .... ...... .... ....... ......... ........ ...... .......",
121 "...... ...... .... ...... .... ....... ......... ........ ...... .......",
122 ". .... ...... .... ...... ... ....... ......... ........ ....... .... .",
123 ".. ....... .... ....... . ....... ......... ........ ........ ..",
124 "................................................................................."
131 static int Open( vlc_object_t
*p_this
)
133 filter_t
*p_filter
= (filter_t
*)p_this
;
137 if( !es_format_IsSimilar( &p_filter
->fmt_in
, &p_filter
->fmt_out
) )
139 msg_Err( p_filter
, "Input and output format does not match" );
143 /* Allocate structure */
144 p_filter
->p_sys
= p_sys
= malloc( sizeof( *p_sys
) );
148 config_ChainParse( p_filter
, CFG_PREFIX
, ppsz_filter_options
,
151 p_sys
->pi_order
= NULL
;
153 vlc_mutex_init( &p_sys
->lock
);
154 p_sys
->change
.i_rows
=
155 var_CreateGetIntegerCommand( p_filter
, CFG_PREFIX
"rows" );
156 p_sys
->change
.i_cols
=
157 var_CreateGetIntegerCommand( p_filter
, CFG_PREFIX
"cols" );
158 p_sys
->change
.b_blackslot
=
159 var_CreateGetBoolCommand( p_filter
, CFG_PREFIX
"black-slot" );
160 p_sys
->b_change
= true;
162 var_AddCallback( p_filter
, CFG_PREFIX
"rows", PuzzleCallback
, p_sys
);
163 var_AddCallback( p_filter
, CFG_PREFIX
"cols", PuzzleCallback
, p_sys
);
164 var_AddCallback( p_filter
, CFG_PREFIX
"black-slot", PuzzleCallback
, p_sys
);
166 p_filter
->pf_video_filter
= Filter
;
167 p_filter
->pf_video_mouse
= Mouse
;
175 static void Close( vlc_object_t
*p_this
)
177 filter_t
*p_filter
= (filter_t
*)p_this
;
178 filter_sys_t
*p_sys
= p_filter
->p_sys
;
180 var_DelCallback( p_filter
, CFG_PREFIX
"rows", PuzzleCallback
, p_sys
);
181 var_DelCallback( p_filter
, CFG_PREFIX
"cols", PuzzleCallback
, p_sys
);
182 var_DelCallback( p_filter
, CFG_PREFIX
"black-slot", PuzzleCallback
, p_sys
);
184 vlc_mutex_destroy( &p_sys
->lock
);
185 free( p_sys
->pi_order
);
193 static picture_t
*Filter( filter_t
*p_filter
, picture_t
*p_pic
)
195 filter_sys_t
*p_sys
= p_filter
->p_sys
;
197 picture_t
*p_outpic
= filter_NewPicture( p_filter
);
200 picture_Release( p_pic
);
205 vlc_mutex_lock( &p_sys
->lock
);
206 if( p_sys
->b_change
)
208 p_sys
->i_rows
= p_sys
->change
.i_rows
;
209 p_sys
->i_cols
= p_sys
->change
.i_cols
;
210 p_sys
->b_blackslot
= p_sys
->change
.b_blackslot
;
211 p_sys
->b_change
= false;
215 vlc_mutex_unlock( &p_sys
->lock
);
218 const int i_rows
= p_sys
->i_rows
;
219 const int i_cols
= p_sys
->i_cols
;
221 /* Draw each piece of the puzzle at the right place */
222 for( int i_plane
= 0; i_plane
< p_outpic
->i_planes
; i_plane
++ )
224 const plane_t
*p_in
= &p_pic
->p
[i_plane
];
225 const int i_pitch
= p_in
->i_pitch
;
226 plane_t
*p_out
= &p_outpic
->p
[i_plane
];
228 for( int i
= 0; i
< i_cols
* i_rows
; i
++ )
230 int i_col
= i
% i_cols
;
231 int i_row
= i
/ i_cols
;
232 int i_ocol
= p_sys
->pi_order
[i
] % i_cols
;
233 int i_orow
= p_sys
->pi_order
[i
] / i_cols
;
234 int i_last_row
= i_row
+ 1;
235 i_orow
*= p_in
->i_lines
/ i_rows
;
236 i_row
*= p_in
->i_lines
/ i_rows
;
237 i_last_row
*= p_in
->i_lines
/ i_rows
;
239 if( p_sys
->b_blackslot
&& !p_sys
->b_finished
&& i
== p_sys
->i_selected
)
241 uint8_t color
= ( i_plane
== Y_PLANE
? 0x0 : 0x80 );
242 for( ; i_row
< i_last_row
; i_row
++, i_orow
++ )
244 memset( p_out
->p_pixels
+ i_row
* i_pitch
+ i_col
* i_pitch
/ i_cols
,
245 color
, i_pitch
/ i_cols
);
250 for( ; i_row
< i_last_row
; i_row
++, i_orow
++ )
252 memcpy( p_out
->p_pixels
+ i_row
* i_pitch
+ i_col
* i_pitch
/ i_cols
,
253 p_in
->p_pixels
+ i_orow
* i_pitch
+ i_ocol
* i_pitch
/ i_cols
,
260 /* Draw the borders of the selected slot */
261 if( p_sys
->i_selected
!= -1 && !p_sys
->b_blackslot
)
263 const plane_t
*p_in
= &p_pic
->p
[Y_PLANE
];
264 const int i_pitch
= p_in
->i_pitch
;
265 plane_t
*p_out
= &p_outpic
->p
[Y_PLANE
];
267 int i_col
= p_sys
->i_selected
% i_cols
;
268 int i_row
= p_sys
->i_selected
/ i_cols
;
269 int i_last_row
= i_row
+ 1;
270 i_row
*= p_in
->i_lines
/ i_rows
;
271 i_last_row
*= p_in
->i_lines
/ i_rows
;
272 memset( p_out
->p_pixels
+ i_row
* i_pitch
+ i_col
* i_pitch
/ i_cols
,
273 0xff, i_pitch
/ i_cols
);
274 for( ; i_row
< i_last_row
; i_row
++ )
276 p_out
->p_pixels
[ i_row
* i_pitch
277 + i_col
* i_pitch
/ i_cols
] = 0xff;
278 p_out
->p_pixels
[ i_row
* i_pitch
279 + (i_col
+1) * i_pitch
/ i_cols
- 1 ] = 0xff;
282 memset( p_out
->p_pixels
+ i_row
* i_pitch
+ i_col
* i_pitch
/ i_cols
,
283 0xff, i_pitch
/ i_cols
);
286 /* Draw the 'Shuffle' button if the puzzle is finished */
287 if( p_sys
->b_finished
)
289 plane_t
*p_out
= &p_outpic
->p
[Y_PLANE
];
290 for( int i
= 0; i
< SHUFFLE_HEIGHT
; i
++ )
292 for( int j
= 0; j
< SHUFFLE_WIDTH
; j
++ )
294 if( shuffle_button
[i
][j
] == '.' )
295 p_out
->p_pixels
[ i
* p_out
->i_pitch
+ j
] = 0xff;
300 return CopyInfoAndRelease( p_outpic
, p_pic
);
303 static int Mouse( filter_t
*p_filter
, vlc_mouse_t
*p_mouse
,
304 const vlc_mouse_t
*p_old
, const vlc_mouse_t
*p_new
)
306 filter_sys_t
*p_sys
= p_filter
->p_sys
;
307 const video_format_t
*p_fmt
= &p_filter
->fmt_in
.video
;
309 /* Only take events inside the puzzle erea */
310 if( p_new
->i_x
< 0 || p_new
->i_x
>= (int)p_fmt
->i_width
||
311 p_new
->i_y
< 0 || p_new
->i_y
>= (int)p_fmt
->i_height
)
315 const bool b_clicked
= vlc_mouse_HasPressed( p_old
, p_new
, MOUSE_BUTTON_LEFT
);
317 /* If the puzzle is finished, shuffle it if needed */
318 if( p_sys
->b_finished
)
321 p_new
->i_x
< SHUFFLE_WIDTH
&& p_new
->i_y
< SHUFFLE_HEIGHT
)
323 p_sys
->b_change
= true;
328 /* This is the only case where we can forward the mouse */
337 const int i_pos_x
= p_new
->i_x
* p_sys
->i_cols
/ p_fmt
->i_width
;
338 const int i_pos_y
= p_new
->i_y
* p_sys
->i_rows
/ p_fmt
->i_height
;
339 const int i_pos
= i_pos_y
* p_sys
->i_cols
+ i_pos_x
;
341 if( p_sys
->i_selected
== -1 )
343 p_sys
->i_selected
= i_pos
;
345 else if( p_sys
->i_selected
== i_pos
&& !p_sys
->b_blackslot
)
347 p_sys
->i_selected
= -1;
349 else if( ( p_sys
->i_selected
== i_pos
+ 1 && p_sys
->i_selected
%p_sys
->i_cols
!= 0 )
350 || ( p_sys
->i_selected
== i_pos
- 1 && i_pos
% p_sys
->i_cols
!= 0 )
351 || p_sys
->i_selected
== i_pos
+ p_sys
->i_cols
352 || p_sys
->i_selected
== i_pos
- p_sys
->i_cols
)
354 /* Swap two pieces */
355 int a
= p_sys
->pi_order
[ p_sys
->i_selected
];
356 p_sys
->pi_order
[ p_sys
->i_selected
] = p_sys
->pi_order
[ i_pos
];
357 p_sys
->pi_order
[ i_pos
] = a
;
359 p_sys
->i_selected
= p_sys
->b_blackslot
? i_pos
: -1;
360 p_sys
->b_finished
= IsFinished( p_sys
);
365 /*****************************************************************************
367 *****************************************************************************/
368 static int PuzzleCallback( vlc_object_t
*p_this
, char const *psz_var
,
369 vlc_value_t oldval
, vlc_value_t newval
,
372 VLC_UNUSED(p_this
); VLC_UNUSED(oldval
);
373 filter_sys_t
*p_sys
= (filter_sys_t
*)p_data
;
375 vlc_mutex_lock( &p_sys
->lock
);
376 if( !strcmp( psz_var
, CFG_PREFIX
"rows" ) )
378 p_sys
->change
.i_rows
= __MAX( 1, newval
.i_int
);
380 else if( !strcmp( psz_var
, CFG_PREFIX
"cols" ) )
382 p_sys
->change
.i_cols
= __MAX( 1, newval
.i_int
);
384 else if( !strcmp( psz_var
, CFG_PREFIX
"black-slot" ) )
386 p_sys
->change
.b_blackslot
= newval
.b_bool
;
388 p_sys
->b_change
= true;
389 vlc_mutex_unlock( &p_sys
->lock
);
394 static bool IsFinished( filter_sys_t
*p_sys
)
396 for( int i
= 0; i
< p_sys
->i_cols
* p_sys
->i_rows
; i
++ )
398 if( i
!= p_sys
->pi_order
[i
] )
404 static bool IsValid( filter_sys_t
*p_sys
)
406 const int i_count
= p_sys
->i_cols
* p_sys
->i_rows
;
408 if( !p_sys
->b_blackslot
)
412 for( int i
= 0; i
< i_count
; i
++ )
414 if( p_sys
->pi_order
[i
] == i_count
- 1 )
416 d
+= i
/ p_sys
->i_cols
+ 1;
419 for( int j
= i
+1; j
< i_count
; j
++ )
421 if( p_sys
->pi_order
[j
] == i_count
- 1 )
423 if( p_sys
->pi_order
[i
] > p_sys
->pi_order
[j
] )
430 static void Shuffle( filter_sys_t
*p_sys
)
432 const int i_count
= p_sys
->i_cols
* p_sys
->i_rows
;
434 free( p_sys
->pi_order
);
436 p_sys
->pi_order
= calloc( i_count
, sizeof(*p_sys
->pi_order
) );
439 for( int i
= 0; i
< i_count
; i
++ )
440 p_sys
->pi_order
[i
] = -1;
442 for( int c
= 0; c
< i_count
; )
444 int i
= rand() % i_count
;
445 if( p_sys
->pi_order
[i
] == -1 )
446 p_sys
->pi_order
[i
] = c
++;
448 p_sys
->b_finished
= IsFinished( p_sys
);
450 } while( p_sys
->b_finished
|| !IsValid( p_sys
) );
452 if( p_sys
->b_blackslot
)
454 for( int i
= 0; i
< i_count
; i
++ )
456 if( p_sys
->pi_order
[i
] == i_count
- 1 )
458 p_sys
->i_selected
= i
;
465 p_sys
->i_selected
= -1;