1 #include "iuse_software_kitten.h"
2 
3 #include <algorithm>
4 #include <cstdlib>  // Needed for rand()
5 #include <functional>
6 #include <string>
7 #include <vector>
8 
9 #include "cuboid_rectangle.h"
10 #include "input.h"
11 #include "optional.h"
12 #include "output.h"
13 #include "posix_time.h"
14 #include "rng.h"
15 #include "text_snippets.h"
16 #include "translations.h"
17 #include "ui_manager.h"
18 
19 static constexpr int EMPTY = -1;
20 static constexpr int ROBOT = 0;
21 static constexpr int KITTEN = 1;
22 
robot_finds_kitten()23 robot_finds_kitten::robot_finds_kitten()
24 {
25     ret = false;
26     char ktile[83] =
27         "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#&()*+./:;=?![]{|}y";
28 
29     nummessages = 201;
30     // NOLINTNEXTLINE(cata-use-named-point-constants)
31     empty.pos = point( -1, -1 );
32     empty.color = nc_color();
33     empty.character = ' ';
34     for( int ( &col )[rfkLINES] : rfkscreen ) {
35         for( int &i : col ) {
36             i = EMPTY;
37         }
38     }
39     /* Create an array to ensure we don't get duplicate messages. */
40     for( int c = 0; c < nummessages; c++ ) {
41         bogus[c] = empty;
42     }
43     /* Now we initialize the various game OBJECTs.
44        * Assign a position to the player. */
45     robot.pos.x = rng( 0, rfkCOLS - 1 );
46     robot.pos.y = rng( 0, rfkLINES - 3 - 1 ) + 3;
47     robot.character = '#';
48     robot.color = c_white;
49     rfkscreen[robot.pos.x][robot.pos.y] = ROBOT;
50 
51     /* Assign the kitten a unique position. */
52     do {
53         kitten.pos.x = rng( 0, rfkCOLS - 1 );
54         kitten.pos.y = rng( 0, rfkLINES - 3 - 1 ) + 3;
55     } while( rfkscreen[kitten.pos.x][kitten.pos.y] != EMPTY );
56 
57     /* Assign the kitten a character and a color. */
58     do {
59         kitten.character = ktile[rng( 0, 81 )];
60     } while( kitten.character == '#' || kitten.character == ' ' );
61 
62     do {
63         kitten.color = all_colors.get_random();
64     } while( kitten.color == c_black );
65 
66     rfkscreen[kitten.pos.x][kitten.pos.y] = KITTEN;
67 
68     /* Now, initialize non-kitten OBJECTs. */
69     for( int c = 0; c < numbogus; c++ ) {
70         /* Assign a unique position. */
71         do {
72             bogus[c].pos.x = rng( 0, rfkCOLS - 1 );
73             bogus[c].pos.y = rng( 0, rfkLINES - 3 - 1 ) + 3;
74         } while( rfkscreen[bogus[c].pos.x][bogus[c].pos.y] != EMPTY );
75         rfkscreen[bogus[c].pos.x][bogus[c].pos.y] = c + 2;
76 
77         /* Assign a character. */
78         do {
79             bogus[c].character = ktile[rng( 0, 81 )];
80         } while( bogus[c].character == '#' || bogus[c].character == ' ' );
81 
82         do {
83             bogus[c].color = all_colors.get_random();
84         } while( bogus[c].color == c_black );
85 
86         /* Assign a unique message. */
87         bool got_nki = false;
88         while( !got_nki ) {
89             std::string nki = SNIPPET.random_from_category( "non_kitten_items" ).value_or(
90                                   translation() ).translated();
91             // Add NKI if it does not already exist in the list
92             if( std::find( bogus_messages.begin(), bogus_messages.end(), nki ) == bogus_messages.end() ) {
93                 bogus_messages.push_back( nki );
94                 got_nki = true;
95             }
96         }
97     }
98 
99     ui_adaptor ui;
100     ui.on_screen_resize( [this]( ui_adaptor & ui ) {
101         bkatwin = catacurses::newwin( rfkLINES + 2, rfkCOLS + 2,
102                                       point( ( TERMX - rfkCOLS - 2 ) / 2, ( TERMY - rfkLINES - 2 ) / 2 ) );
103         w = catacurses::newwin( rfkLINES, rfkCOLS,
104                                 point( ( TERMX - rfkCOLS ) / 2, ( TERMY - rfkLINES ) / 2 ) );
105         ui.position_from_window( bkatwin );
106     } );
107     ui.mark_resize();
108 
109     ui.on_redraw( [this]( const ui_adaptor & ) {
110         show();
111     } );
112 
113     /* Now the fun begins. */
114     current_ui_state = ui_state::instructions;
115     while( current_ui_state != ui_state::exit ) {
116         ui_manager::redraw();
117         process_input();
118     }
119 }
120 
show() const121 void robot_finds_kitten::show() const
122 {
123     input_context ctxt( "IUSE_SOFTWARE_KITTEN" );
124 
125     draw_border( bkatwin );
126     wnoutrefresh( bkatwin );
127 
128     werase( w );
129     if( current_ui_state != ui_state::instructions ) {
130         for( int c = 0; c < rfkCOLS; c++ ) {
131             mvwputch( w, point( c, 2 ), BORDER_COLOR, '_' );
132         }
133         wmove( w, kitten.pos );
134         draw_kitten();
135 
136         for( int c = 0; c < numbogus; c++ ) {
137             mvwputch( w, bogus[c].pos, bogus[c].color, bogus[c].character );
138         }
139 
140         wmove( w, robot.pos );
141         draw_robot();
142     }
143     switch( current_ui_state ) {
144         case ui_state::instructions: {
145             int pos = 1;
146             // NOLINTNEXTLINE(cata-use-named-point-constants)
147             pos += fold_and_print( w, point( 1, 0 ), getmaxx( w ) - 4, c_light_gray,
148                                    _( "robotfindskitten v22July2008" ) );
149             pos += 1 + fold_and_print( w, point( 1, pos ), getmaxx( w ) - 4, c_light_gray,
150                                        _( "Originally by the illustrious Leonard Richardson, "
151                                           "rewritten in PDCurses by Joseph Larson, "
152                                           "ported to CDDA gaming system by a nutcase." ) );
153 
154             pos += 1 + fold_and_print( w, point( 1, pos ), getmaxx( w ) - 4, c_light_gray,
155                                        _( "In this game, you are robot (" ) );
156             draw_robot();
157             wprintz( w, c_light_gray, _( ")." ) );
158             pos += 1 + fold_and_print( w, point( 1, pos ), getmaxx( w ) - 4, c_light_gray,
159                                        _( "Your job is to find kitten.  This task is complicated by the existence of various things "
160                                           "which are not kitten.  Robot must touch items to determine if they are kitten or not.  "
161                                           "The game ends when robot finds kitten.  Alternatively, you may end the game by hitting %s." ),
162                                        ctxt.get_desc( "QUIT" ) );
163             fold_and_print( w, point( 1, pos ), getmaxx( w ) - 4, c_light_gray,
164                             _( "Press any key to start." ) );
165             break;
166         }
167         case ui_state::main:
168             mvwprintz( w, point_zero, c_white, _( "robotfindskitten v22July2008 - press %s to quit." ),
169                        ctxt.get_desc( "QUIT" ) );
170             break;
171         case ui_state::invalid_input:
172             mvwprintz( w, point_zero, c_white, _( "Invalid command: Use direction keys or press %s to quit." ),
173                        ctxt.get_desc( "QUIT" ) );
174             break;
175         case ui_state::bogus_message: {
176             std::vector<std::string> bogusvstr = foldstring( this_bogus_message, rfkCOLS );
177             for( size_t c = 0; c < bogusvstr.size(); c++ ) {
178                 mvwprintz( w, point( 0, c ), c_white, bogusvstr[c] );
179             }
180             break;
181         }
182         case ui_state::end_animation: {
183             /* The grand cinema scene. */
184             const int c = std::min( end_animation_frame, 3 );
185             wmove( w, point( rfkCOLS / 2 - 4 + c, 1 ) );
186             if( end_animation_last_input_left_or_up ) {
187                 draw_kitten();
188             } else {
189                 draw_robot();
190             }
191             wmove( w, point( rfkCOLS / 2 + 3 - c, 1 ) );
192             if( end_animation_last_input_left_or_up ) {
193                 draw_robot();
194             } else {
195                 draw_kitten();
196             }
197             if( end_animation_frame >= 4 ) {
198                 /* They're in love! */
199                 mvwprintz( w, point( ( rfkCOLS - 6 ) / 2 - 1, 0 ), c_light_red, "<3<3<3" );
200             }
201             if( end_animation_frame >= 5 ) {
202                 mvwprintz( w, point_zero, c_white, _( "You found kitten!  Way to go, robot!" ) );
203             }
204             break;
205         }
206         case ui_state::exit:
207             break;
208     }
209     wnoutrefresh( w );
210 }
211 
process_input()212 void robot_finds_kitten::process_input()
213 {
214     input_context ctxt( "IUSE_SOFTWARE_KITTEN" );
215     ctxt.register_action( "QUIT" );
216     ctxt.register_action( "HELP_KEYBINDINGS" );
217     ctxt.register_action( "ANY_INPUT" );
218     switch( current_ui_state ) {
219         case ui_state::instructions: {
220             const std::string action = ctxt.handle_input();
221             if( action == "QUIT" ) {
222                 current_ui_state = ui_state::exit;
223             } else if( action == "ANY_INPUT" && !ctxt.get_raw_input().sequence.empty() ) {
224                 current_ui_state = ui_state::main;
225             }
226             break;
227         }
228         case ui_state::main:
229         case ui_state::invalid_input:
230         case ui_state::bogus_message: {
231             ctxt.register_cardinal();
232             const std::string action = ctxt.handle_input();
233             if( action == "QUIT" ) {
234                 current_ui_state = ui_state::exit;
235             } else if( action == "ANY_INPUT" ) {
236                 if( !ctxt.get_raw_input().sequence.empty() ) {
237                     current_ui_state = ui_state::invalid_input;
238                 }
239             } else if( action != "HELP_KEYBINDINGS" ) {
240                 current_ui_state = ui_state::main;
241                 point check = robot.pos;
242 
243                 if( action == "UP" ) {
244                     check.y--;
245                 } else if( action == "DOWN" ) {
246                     check.y++;
247                 } else if( action == "LEFT" ) {
248                     check.x--;
249                 } else if( action == "RIGHT" ) {
250                     check.x++;
251                 }
252 
253                 constexpr half_open_rectangle<point> bounds(
254                     point( 0, 3 ), point( rfkCOLS, rfkLINES ) );
255                 if( !bounds.contains( check ) ) {
256                     /* Can't move past edge */
257                 } else if( rfkscreen[check.x][check.y] != EMPTY ) {
258                     switch( rfkscreen[check.x][check.y] ) {
259                         case ROBOT:
260                             /* We didn't move. */
261                             break;
262                         case KITTEN:
263                             /* Found it! */
264                             ret = true;
265                             current_ui_state = ui_state::end_animation;
266                             end_animation_frame = 0;
267                             end_animation_last_input_left_or_up = action == "UP" || action == "LEFT";
268                             break;
269                         default:
270                             current_ui_state = ui_state::bogus_message;
271                             this_bogus_message = bogus_messages.at( rfkscreen[check.x][check.y] - 2 );
272                             break;
273                     }
274                 } else {
275                     /* Otherwise, move the robot. */
276                     rfkscreen[robot.pos.x][robot.pos.y] = EMPTY;
277                     robot.pos = check;
278                     rfkscreen[robot.pos.x][robot.pos.y] = ROBOT;
279                 }
280             }
281             break;
282         }
283         case ui_state::end_animation:
284             if( end_animation_frame + 1 == num_end_animation_frames ) {
285                 const std::string action = ctxt.handle_input();
286                 if( action != "HELP_KEYBINDINGS" ) {
287                     current_ui_state = ui_state::exit;
288                 }
289             } else {
290                 refresh_display();
291                 end_animation_frame++;
292                 timespec ts;
293                 ts.tv_sec = 1;
294                 ts.tv_nsec = 0;
295                 nanosleep( &ts, nullptr );
296             }
297             break;
298         case ui_state::exit:
299             break;
300     }
301 }
302 
303 /* Draws robot at current position */
draw_robot() const304 void robot_finds_kitten::draw_robot() const
305 {
306     wputch( w, robot.color, robot.character );
307 }
308 
309 /* Draws kitten at current position */
draw_kitten() const310 void robot_finds_kitten::draw_kitten() const
311 {
312     wputch( w, kitten.color, kitten.character );
313 }
314