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