gemwarrior 0.15.10 → 0.15.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,705 +1,705 @@
1
- # lib/gemwarrior/repl.rb
2
- # My own, simple, Read Evaluate Print Loop module
3
-
4
- require 'readline'
5
- require 'os'
6
- require 'clocker'
7
- require 'io/console'
8
- require 'gems'
9
- require 'ap'
10
-
11
- require_relative 'misc/audio'
12
- require_relative 'misc/timer'
13
- require_relative 'misc/wordlist'
14
- require_relative 'evaluator'
15
- require_relative 'game_options'
16
- require_relative 'version'
17
-
18
- module Gemwarrior
19
- class Repl
20
- # CONSTANTS
21
- SCREEN_WIDTH_MIN = 80
22
- SCREEN_WIDTH_MAX = 120
23
- QUIT_MESSAGE = 'Temporal flux detected. Shutting down...'.colorize(:red)
24
- MAIN_MENU_QUIT_MESSAGE = 'Giving up so soon? Jool will be waiting...'.colorize(:red)
25
- SPLASH_MESSAGE = 'Welcome to *Jool*, where randomized fortune is just as likely as mayhem.'
26
- GITHUB_NAME = 'michaelchadwick'
27
- GITHUB_PROJECT = 'gemwarrior'
28
- ERROR_SOUND_NOT_ENABLED = 'Sound is disabled! Enter \'x\' to exit'
29
-
30
- attr_accessor :game, :world, :evaluator
31
-
32
- def initialize(game, world, evaluator)
33
- self.game = game
34
- self.world = world
35
- self.evaluator = evaluator
36
-
37
- GameOptions.data['wrap_width'] = get_screen_width
38
- end
39
-
40
- def get_screen_width
41
- screen_width = SCREEN_WIDTH_MIN
42
-
43
- begin
44
- require 'io/console'
45
- screen_width = IO.console.winsize[1]
46
- rescue
47
- if command_exists?('tput')
48
- screen_width = `tput cols`.to_i
49
- elsif command_exists?('stty')
50
- screen_width = `stty size`.split.last.to_i
51
- elsif command_exists?('mode')
52
- mode_output = `mode`.split
53
- screen_width = mode_output[mode_output.index('Columns:')+1].to_i
54
- end
55
- end
56
-
57
- case
58
- when screen_width.nil?, screen_width <= 0
59
- return SCREEN_WIDTH_MIN
60
- else
61
- return [screen_width, SCREEN_WIDTH_MAX].min
62
- end
63
- end
64
-
65
- def start(initial_command, extra_command, new_skip, resume_skip)
66
- setup_screen(initial_command, extra_command, new_skip, resume_skip)
67
-
68
- clocker = Clocker.new
69
-
70
- at_exit do
71
- update_duration(clocker.stop)
72
- game.update_options_file
73
- log_stats(world.duration, world.player)
74
- save_game(world)
75
- end
76
-
77
- clocker.clock do
78
- # main loop
79
- loop do
80
- prompt
81
- begin
82
- main_loop
83
- rescue Interrupt
84
- puts
85
- puts QUIT_MESSAGE
86
- exit
87
- end
88
- end
89
- end
90
- end
91
-
92
- def main_loop(ext_input = nil)
93
- input = ext_input.nil? ? read_line : ext_input
94
- result = evaluator.parse(input)
95
- if result.eql?('exit')
96
- exit
97
- elsif result.eql?('checkupdate')
98
- check_for_new_release
99
- else
100
- puts result
101
- end
102
- end
103
-
104
- # timer observer
105
- #def update(command)
106
- # main_loop(command)
107
- #end
108
-
109
- private
110
-
111
- def clear_screen
112
- OS.windows? ? system('cls') : system('clear')
113
- end
114
-
115
- def read_line
116
- prompt_text = GameOptions.data['debug_mode'] ? ' GW[D]> ' : ' GW> '
117
- Readline.readline(prompt_text, true).to_s
118
- end
119
-
120
- def get_save_file_name
121
- if save_file_exist?
122
- File.open(GameOptions.data['save_file_yaml_path'], 'r') do |f|
123
- return YAML.load(f)
124
- end
125
- end
126
- end
127
-
128
- def puts(s = '', width = GameOptions.data['wrap_width'])
129
- super s.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n") unless s.nil?
130
- end
131
-
132
- def print_logo
133
- puts '/-+-+-+ +-+-+-+-+-+-+-\\'.colorize(:yellow)
134
- puts '|G|E|M| |W|A|R|R|I|O|R|'.colorize(:yellow)
135
- puts '\\-+-+-+ +-+-+-+-+-+-+-/'.colorize(:yellow)
136
- puts '[[[[[[[DEBUGGING]]]]]]]'.colorize(:white) if GameOptions.data['debug_mode']
137
- end
138
-
139
- def print_splash_message
140
- SPLASH_MESSAGE.length.times { print '=' }
141
- puts
142
- puts SPLASH_MESSAGE
143
- SPLASH_MESSAGE.length.times { print '=' }
144
- puts
145
- end
146
-
147
- def print_fortune
148
- noun1_values = WordList.new('noun-plural')
149
- noun2_values = WordList.new('noun-plural')
150
- noun3_values = WordList.new('noun-plural')
151
-
152
- puts "* Remember: #{noun1_values.get_random_value.colorize(:yellow)} and #{noun2_values.get_random_value.colorize(:yellow)} are the key to #{noun3_values.get_random_value.colorize(:yellow)} *\n\n"
153
- puts
154
- end
155
-
156
- def print_about_text
157
- puts 'Gem Warrior - A Game of Fortune and Mayhem'.colorize(:yellow)
158
- puts '=========================================='.colorize(:yellow)
159
- puts 'Gem Warrior is a text adventure roguelike-lite as a RubyGem created by Michael Chadwick (mike@neb.host) and released as open-source on Github. Take on the task set by Queen Ruby to defeat the evil Emerald and get back the ShinyThing(tm) he stole for terrible, dastardly reasons.'
160
- puts
161
- puts 'Explore the land of Jool with the power of text, fighting enemies to improve your station, grabbing curious items that may or may not come in handy, and finally defeating Mr. Emerald himself to win the game.'
162
- end
163
-
164
- def print_help_text
165
- puts 'Gem Warrior - Some Basic Help Commands'.colorize(:yellow)
166
- puts '======================================'.colorize(:yellow)
167
- puts '* Basic functions: look, go, character, inventory, attack *'
168
- puts '* Type \'help\' while in-game for complete list of commands *'
169
- puts '* Most commands can be abbreviated to their first letter *'
170
- puts '* Note: if something isn\'t working, try: *'
171
- puts '* 1) starting a new game *'
172
- puts '* 2) updating the game *'
173
- end
174
-
175
- def display_log_of_attempts
176
- if File.exist?(GameOptions.data['log_file_path']) and !File.zero?(GameOptions.data['log_file_path'])
177
- File.open(GameOptions.data['log_file_path']).readlines.each do |line|
178
- print "#{line}"
179
- end
180
- if GameOptions.data['debug_mode']
181
- print 'Clear log of attempts? (y/n) '
182
- answer = gets.chomp.downcase
183
-
184
- case answer
185
- when 'y', 'yes'
186
- File.truncate(GameOptions.data['log_file_path'], 0)
187
- puts 'Log of attempts: erased!'
188
- end
189
-
190
- puts
191
- end
192
- else
193
- puts 'No attempts made yet!'
194
- end
195
- end
196
-
197
- def check_for_new_release
198
- new_release_available = false
199
- puts 'Checking releases...'
200
- remote_release = Gems.versions('gemwarrior').first['number']
201
- local_release = Gemwarrior::VERSION
202
-
203
- 0.upto(2) do |i|
204
- if remote_release.split('.')[i].to_i > local_release.split('.')[i].to_i
205
- new_release_available = true
206
- end
207
- end
208
-
209
- if new_release_available
210
- puts "GW v#{remote_release} available! Please exit and run 'gem update' before continuing."
211
- puts
212
- else
213
- puts 'You have the latest version. Fantastic!'
214
- puts
215
- end
216
- end
217
-
218
- def print_options_sound_sytem
219
- puts
220
- puts 'Sound System Selection'.colorize(:yellow)
221
- puts '================================='.colorize(:yellow)
222
- puts
223
- win32 = ' (1) WIN32-SOUND '
224
- OS.windows? ? (print win32) : (print win32.colorize(:light_black))
225
- print '(SELECTED)'.colorize(:yellow) if GameOptions.data['sound_system'].eql?('win32-sound')
226
- print "\n"
227
- print ' (2) FEEP '
228
- print '(SELECTED)'.colorize(:yellow) if GameOptions.data['sound_system'].eql?('feep')
229
- print "\n"
230
- print ' (3) BLOOPS '
231
- print '(SELECTED)'.colorize(:yellow) if GameOptions.data['sound_system'].eql?('bloops')
232
- print "\n"
233
- print "\n"
234
- print ' (T) TEST SOUND '
235
- print "\n"
236
- puts
237
- puts ' WIN32-SOUND : good quality; Windows-only'
238
- puts ' FEEP : cross-platform; VERY SLOW and BUGGY'
239
- puts ' BLOOPS : cross-platform; requires portaudio'
240
- puts
241
- puts ' NOTE: none of these are required dependencies anymore, as sound is optional.'
242
- puts ' If you do not have the one selected installed on your machine, you will get '
243
- puts ' an error on game load, and no sound will be heard.'
244
- puts
245
- puts '================================='.colorize(:yellow)
246
- puts
247
- puts 'Enter option number to select sound system, or any other key to exit.'
248
- puts
249
- print '[GW_OPTS]-[SOUND_SYSTEM]> '
250
-
251
- answer = STDIN.getch.chomp.downcase
252
-
253
- case answer
254
- when '1'
255
- if OS.windows?
256
- puts answer
257
- GameOptions.add 'sound_system', 'win32-sound'
258
- else
259
- puts
260
- end
261
- print_options_sound_sytem
262
- when '2'
263
- puts answer
264
- GameOptions.add 'sound_system', 'feep'
265
- print_options_sound_sytem
266
- when '3'
267
- puts answer
268
- GameOptions.add 'sound_system', 'bloops'
269
- print_options_sound_sytem
270
- when 't'
271
- puts answer
272
- Audio.init
273
- play_test_tune
274
- print_errors
275
- print_options_sound_sytem
276
- else
277
- puts
278
- return
279
- end
280
- end
281
-
282
- def print_options
283
- puts
284
- puts 'Gem Warrior General Options'.colorize(:yellow)
285
- puts '================================='.colorize(:yellow)
286
- puts
287
- puts 'Change several sound options, whether Wordnik is used to generate more dynamic descriptors of entities (valid WORDNIK_API_KEY environment variable must be set), and if attack/fight commands need to have a target or not (if enabled, will attack first monster in vicinty).'
288
- puts
289
- puts " (1) SOUND ENABLED : #{GameOptions.data['sound_enabled']}"
290
- puts " (2) SOUND SYSTEM : #{GameOptions.data['sound_system']}"
291
- puts " (3) SOUND VOLUME : #{GameOptions.data['sound_volume']}"
292
- puts " (4) USE WORDNIK : #{GameOptions.data['use_wordnik']}"
293
- puts " (5) FIGHT COMPLETION : #{GameOptions.data['fight_completion']}"
294
- puts
295
- puts '================================='.colorize(:yellow)
296
- puts
297
- puts 'Enter option number to change value, or any other key to return to main menu.'
298
- puts
299
- print '[GW_OPTS]> '
300
-
301
- answer = STDIN.getch
302
-
303
- case answer
304
- when '1'
305
- print answer
306
- GameOptions.data['sound_enabled'] = !GameOptions.data['sound_enabled']
307
- print_options
308
- when '2'
309
- print answer
310
- print "\n"
311
- print_options_sound_sytem
312
- print_options
313
- when '3'
314
- print answer
315
- print "\n"
316
- print 'Enter a volume from 0.0 to 1.0: '
317
- new_vol = gets.chomp.to_f.abs
318
- if new_vol >= 0.0 and new_vol <= 1.0
319
- GameOptions.data['sound_volume'] = new_vol
320
- else
321
- puts 'Not a valid volume.'
322
- end
323
- print_options
324
- when '4'
325
- print answer
326
- GameOptions.data['use_wordnik'] = !GameOptions.data['use_wordnik']
327
- print_options
328
- when '5'
329
- print answer
330
- GameOptions.data['fight_completion'] = !GameOptions.data['fight_completion']
331
- print_options
332
- else
333
- print answer
334
- return
335
- end
336
- end
337
-
338
- def print_sound_test
339
- puts
340
- puts 'Gem Warrior Sound Test'.colorize(:yellow)
341
- puts '================================='.colorize(:yellow)
342
-
343
- if GameOptions.data['sound_enabled']
344
- puts
345
- puts 'Play all the sounds in Gemwarrior.'
346
- puts
347
-
348
- cues = Audio.get_cues
349
- for cue in cues do
350
- pp cue[0].to_s
351
- end
352
-
353
- puts
354
- puts '================================='.colorize(:yellow)
355
- puts
356
- puts 'Enter sound id to play it, or enter "x" to return to the main menu.'
357
- else
358
- GameOptions.data['errors'] = ERROR_SOUND_NOT_ENABLED
359
- end
360
-
361
- print_errors
362
-
363
- puts
364
- print '[GW_SOUND_TEST]> '
365
-
366
- answer = gets.chomp.downcase
367
-
368
- # print answer
369
- if answer.eql?('x')
370
- return
371
- else
372
- Audio.play_synth(answer.to_sym)
373
- print_sound_test
374
- end
375
- end
376
-
377
- def print_main_menu
378
- puts
379
- puts " GW v#{Gemwarrior::VERSION}"
380
- puts '======================='
381
-
382
- save_file = get_save_file_name
383
- if not save_file.nil?
384
- recent_name = save_file.instance_variable_get(:@player).name
385
- puts " #{'(R)esume Game'.colorize(:green)} as #{recent_name.colorize(:yellow)}"
386
- end
387
-
388
- puts ' (N)ew Game'
389
- puts ' (A)bout'
390
- puts ' (H)elp'
391
- puts ' (O)ptions'
392
- puts ' (L)og of Attempts'
393
- puts ' (S)ound Test'
394
- puts ' (C)heck for Updates'
395
- puts ' (E)xit'.colorize(:red)
396
- puts '======================='
397
- puts
398
- end
399
-
400
- def print_main_menu_prompt
401
- print '> '
402
- end
403
-
404
- def run_main_menu(show_choices = true)
405
- print_main_menu if show_choices
406
- print_main_menu_prompt if show_choices
407
-
408
- choice = STDIN.getch.downcase
409
-
410
- case choice
411
- when 'n'
412
- if overwrite_save?
413
- clear_screen
414
- play_intro_tune
415
- print_splash_message
416
- print_fortune
417
- return
418
- else
419
- run_main_menu
420
- end
421
- when 'r'
422
- if save_file_exist?
423
- result = resume_game
424
- if result.nil?
425
- run_main_menu
426
- else
427
- print_errors
428
- play_resume_tune
429
- load_saved_world(result)
430
- return
431
- end
432
- end
433
- when 'a'
434
- puts choice
435
- print_about_text
436
- run_main_menu
437
- when 'h'
438
- puts choice
439
- print_help_text
440
- run_main_menu
441
- when 'o'
442
- puts choice
443
- print_options
444
- run_main_menu
445
- when 'l'
446
- puts choice
447
- display_log_of_attempts
448
- run_main_menu
449
- when 's'
450
- puts choice
451
- print_sound_test
452
- run_main_menu
453
- when 'c'
454
- puts choice
455
- check_for_new_release
456
- run_main_menu
457
- when 'e', 'x', 'q'
458
- puts choice
459
- puts MAIN_MENU_QUIT_MESSAGE
460
- game.update_options_file
461
- exit
462
- when "\c?" # Backspace/Delete
463
- refresh_menu
464
- when "\e" # ANSI escape sequence
465
- case STDIN.getch
466
- when '[' # CSI
467
- choice = STDIN.getch
468
- puts choice
469
- case choice
470
- when 'A', 'B', 'C', 'D' # arrow keys
471
- refresh_menu
472
- end
473
- end
474
- else # All other invalid options
475
- refresh_menu
476
- end
477
- end
478
-
479
- # need this to handle any non-valid input at the main menu
480
- def refresh_menu
481
- clear_screen
482
- print_logo
483
- run_main_menu
484
- end
485
-
486
- def log_stats(duration, pl)
487
- # display stats upon exit
488
- Hr.print('#')
489
- print 'Gem Warrior'.colorize(color: :white, background: :black)
490
- print " v#{Gemwarrior::VERSION}".colorize(:yellow)
491
- print " played for #{duration[:mins].to_s.colorize(color: :white, background: :black)} min(s)"
492
- print ", #{duration[:secs].to_s.colorize(color: :white, background: :black)} sec(s)"
493
- # print ", and #{duration[:ms].to_s.colorize(color: :white, background: :black)} ms"
494
- print "\n"
495
- Hr.print('-')
496
- print "#{pl.name.ljust(10).colorize(:green)} "
497
- print "destroyed #{pl.monsters_killed.to_s.colorize(color: :yellow, background: :black)} monster(s)"
498
- print "\n".ljust(12)
499
- print "destroyed #{pl.bosses_killed.to_s.colorize(color: :yellow, background: :black)} boss(es)"
500
- print "\n".ljust(12)
501
- print "picked up #{pl.items_taken.to_s.colorize(color: :yellow, background: :black)} item(s)"
502
- print "\n".ljust(12)
503
- print "traveled #{pl.movements_made.to_s.colorize(color: :yellow, background: :black)} time(s)"
504
- print "\n".ljust(12)
505
- print "rested #{pl.rests_taken.to_s.colorize(color: :yellow, background: :black)} time(s)"
506
- print "\n".ljust(12)
507
- print "died #{pl.deaths.to_s.colorize(color: :yellow, background: :black)} time(s)"
508
- print "\n"
509
- Hr.print('#')
510
-
511
- # log stats to file in home directory
512
- File.open(GameOptions.data['log_file_path'], 'a') do |f|
513
- f.write "#{Time.now} #{pl.name.rjust(13)} - V:#{Gemwarrior::VERSION} LV:#{pl.level} XP:#{pl.xp} $:#{pl.rox} MK:#{pl.monsters_killed} BK:#{pl.bosses_killed} ITM:#{pl.items_taken} MOV:#{pl.movements_made} RST:#{pl.rests_taken} DTH:#{pl.deaths}\n"
514
- end
515
- end
516
-
517
- def save_game(world)
518
- mode = GameOptions.data['save_file_mode']
519
- puts 'Saving game...'
520
-
521
- if mode.eql? 'Y'
522
- File.open(GameOptions.data['save_file_yaml_path'], 'w') do |f|
523
- f.write YAML.dump(world)
524
- end
525
- elsif mode.eql? 'M'
526
- File.open(GameOptions.data['save_file_bin_path'], 'w') do |f|
527
- f.write Marshal.dump(world)
528
- end
529
- else
530
- puts 'Error: Save file mode not set. Game not saved.'
531
- return
532
- end
533
- puts 'Game saved!'
534
- end
535
-
536
- def save_file_exist?
537
- mode = GameOptions.data['save_file_mode']
538
- if mode.eql? 'Y'
539
- File.exist?(GameOptions.data['save_file_yaml_path'])
540
- elsif mode.eql? 'M'
541
- File.exist?(GameOptions.data['save_file_bin_path'])
542
- else
543
- false
544
- end
545
- end
546
-
547
- def resume_game
548
- mode = GameOptions.data['save_file_mode']
549
- puts 'Resuming game...'
550
-
551
- if mode.eql? 'Y'
552
- if File.exist?(GameOptions.data['save_file_yaml_path'])
553
- File.open(GameOptions.data['save_file_yaml_path'], 'r') do |f|
554
- return YAML.load(f)
555
- end
556
- else
557
- puts 'No save file exists.'
558
- nil
559
- end
560
- elsif mode.eql? 'M'
561
- if File.exist?(GameOptions.data['save_file_marshal_path'])
562
- File.open(GameOptions.data['save_file_marshal_path'], 'r') do |f|
563
- return Marshal.load(f)
564
- end
565
- else
566
- puts 'No save file exists.'
567
- nil
568
- end
569
- end
570
- end
571
-
572
- def overwrite_save?
573
- mode = GameOptions.data['save_file_mode']
574
- save_file_path = ''
575
-
576
- if mode.eql? 'Y'
577
- save_file_path = GameOptions.data['save_file_yaml_path']
578
- elsif mode.eql? 'M'
579
- save_file_path = GameOptions.data['save_file_marshal_path']
580
- end
581
-
582
- if File.exist?(save_file_path)
583
- print 'Overwrite existing save file? (y/n) '
584
- answer = gets.chomp.downcase
585
-
586
- case answer
587
- when 'y', 'yes'
588
- puts 'New game started! Press any key to continue.'
589
- gets
590
- return true
591
- else
592
- puts 'New game aborted.'
593
- return false
594
- end
595
- end
596
- true
597
- end
598
-
599
- def update_duration(new_duration)
600
- new_mins = new_duration[:mins]
601
- new_secs = new_duration[:secs]
602
- new_ms = new_duration[:ms]
603
-
604
- world.duration[:mins] += new_mins
605
- world.duration[:secs] += new_secs
606
- world.duration[:ms] += new_ms
607
-
608
- if world.duration[:ms] > 1000
609
- world.duration[:secs] += world.duration[:ms] / 1000
610
- world.duration[:ms] = world.duration[:ms] % 1000
611
- end
612
-
613
- if world.duration[:secs] > 60
614
- world.duration[:mins] += world.duration[:secs] / 60
615
- world.duration[:secs] = world.duration[:secs] % 60
616
- end
617
- end
618
-
619
- def load_saved_world(result)
620
- self.world = result
621
- self.evaluator = Evaluator.new(self.world)
622
- end
623
-
624
- def setup_screen(initial_command = nil, extra_command = nil, new_skip = false, resume_skip = false)
625
- # welcome player to game
626
- clear_screen
627
- print_logo
628
-
629
- # main menu loop until new game or exit
630
- if new_skip
631
- print_errors
632
- play_intro_tune
633
- print_splash_message
634
- print_fortune
635
- elsif resume_skip
636
- result = resume_game
637
- if result.nil?
638
- run_main_menu
639
- else
640
- print_errors
641
- load_saved_world(result)
642
- end
643
- else
644
- run_main_menu
645
- end
646
-
647
- # hook to do something right off the bat
648
- puts evaluator.parse(initial_command) unless initial_command.nil?
649
- puts evaluator.parse(extra_command) unless extra_command.nil?
650
- end
651
-
652
- def print_errors
653
- if GameOptions.data['errors']
654
- puts "\n\n"
655
- puts "Errors: #{GameOptions.data['errors'].colorize(:red)}"
656
- GameOptions.data['errors'] = nil
657
- end
658
- end
659
-
660
- def play_intro_tune
661
- Audio.play_synth(:intro)
662
- end
663
-
664
- def play_resume_tune
665
- Audio.play_synth(:resume_game)
666
- end
667
-
668
- def play_test_tune
669
- Audio.play_synth(:test)
670
- end
671
-
672
- def prompt
673
- prompt_template = "\n"
674
- prompt_template += "[LV:%2s][XP:%3s][ROX:%3s][HP:%3s/%-3s] [".colorize(:yellow)
675
- prompt_template += "%s".colorize(:green)
676
- prompt_template += " @ ".colorize(:yellow)
677
- prompt_template += "%s".colorize(:cyan)
678
- prompt_template += "]".colorize(:yellow)
679
- prompt_template += "[%s, %s, %s]".colorize(:yellow) if GameOptions.data['debug_mode']
680
- prompt_template += "\n"
681
- prompt_template += '[c:character][i:inventory][l:look][u:use][t:take]'
682
-
683
- prompt_vars_arr = [
684
- world.player.level,
685
- world.player.xp,
686
- world.player.rox,
687
- world.player.hp_cur,
688
- world.player.hp_max,
689
- world.player.name,
690
- world.location_by_coords(world.player.cur_coords).name_display
691
- ]
692
- if GameOptions.data['debug_mode']
693
- prompt_vars_arr.push(world.player.cur_coords[:x], world.player.cur_coords[:y], world.player.cur_coords[:z])
694
- end
695
- print (prompt_template % prompt_vars_arr)
696
- print "\n"
697
- end
698
-
699
- def command_exists?(cmd)
700
- ENV['PATH'].split(File::PATH_SEPARATOR).collect { |d|
701
- Dir.entries d if Dir.exist? d
702
- }.flatten.include?(cmd)
703
- end
704
- end
705
- end
1
+ # lib/gemwarrior/repl.rb
2
+ # My own, simple, Read Evaluate Print Loop module
3
+
4
+ require 'readline'
5
+ require 'os'
6
+ require 'clocker'
7
+ require 'io/console'
8
+ require 'gems'
9
+ require 'ap'
10
+
11
+ require_relative 'misc/audio'
12
+ require_relative 'misc/timer'
13
+ require_relative 'misc/wordlist'
14
+ require_relative 'evaluator'
15
+ require_relative 'game_options'
16
+ require_relative 'version'
17
+
18
+ module Gemwarrior
19
+ class Repl
20
+ # CONSTANTS
21
+ SCREEN_WIDTH_MIN = 80
22
+ SCREEN_WIDTH_MAX = 120
23
+ QUIT_MESSAGE = 'Temporal flux detected. Shutting down...'.colorize(:red)
24
+ MAIN_MENU_QUIT_MESSAGE = 'Giving up so soon? Jool will be waiting...'.colorize(:red)
25
+ SPLASH_MESSAGE = 'Welcome to *Jool*, where randomized fortune is just as likely as mayhem.'
26
+ GITHUB_NAME = 'michaelchadwick'
27
+ GITHUB_PROJECT = 'gemwarrior'
28
+ ERROR_SOUND_NOT_ENABLED = 'Sound is disabled! Enter \'x\' to exit'
29
+
30
+ attr_accessor :game, :world, :evaluator
31
+
32
+ def initialize(game, world, evaluator)
33
+ self.game = game
34
+ self.world = world
35
+ self.evaluator = evaluator
36
+
37
+ GameOptions.data['wrap_width'] = get_screen_width
38
+ end
39
+
40
+ def get_screen_width
41
+ screen_width = SCREEN_WIDTH_MIN
42
+
43
+ begin
44
+ require 'io/console'
45
+ screen_width = IO.console.winsize[1]
46
+ rescue
47
+ if command_exists?('tput')
48
+ screen_width = `tput cols`.to_i
49
+ elsif command_exists?('stty')
50
+ screen_width = `stty size`.split.last.to_i
51
+ elsif command_exists?('mode')
52
+ mode_output = `mode`.split
53
+ screen_width = mode_output[mode_output.index('Columns:')+1].to_i
54
+ end
55
+ end
56
+
57
+ case
58
+ when screen_width.nil?, screen_width <= 0
59
+ return SCREEN_WIDTH_MIN
60
+ else
61
+ return [screen_width, SCREEN_WIDTH_MAX].min
62
+ end
63
+ end
64
+
65
+ def start(initial_command, extra_command, new_skip, resume_skip)
66
+ setup_screen(initial_command, extra_command, new_skip, resume_skip)
67
+
68
+ clocker = Clocker.new
69
+
70
+ at_exit do
71
+ update_duration(clocker.stop)
72
+ game.update_options_file
73
+ log_stats(world.duration, world.player)
74
+ save_game(world)
75
+ end
76
+
77
+ clocker.clock do
78
+ # main loop
79
+ loop do
80
+ prompt
81
+ begin
82
+ main_loop
83
+ rescue Interrupt
84
+ puts
85
+ puts QUIT_MESSAGE
86
+ exit
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ def main_loop(ext_input = nil)
93
+ input = ext_input.nil? ? read_line : ext_input
94
+ result = evaluator.parse(input)
95
+ if result.eql?('exit')
96
+ exit
97
+ elsif result.eql?('checkupdate')
98
+ check_for_new_release
99
+ else
100
+ puts result
101
+ end
102
+ end
103
+
104
+ # timer observer
105
+ #def update(command)
106
+ # main_loop(command)
107
+ #end
108
+
109
+ private
110
+
111
+ def clear_screen
112
+ OS.windows? ? system('cls') : system('clear')
113
+ end
114
+
115
+ def read_line
116
+ prompt_text = GameOptions.data['debug_mode'] ? ' GW[D]> ' : ' GW> '
117
+ Readline.readline(prompt_text, true).to_s
118
+ end
119
+
120
+ def get_save_file_name
121
+ if save_file_exist?
122
+ File.open(GameOptions.data['save_file_yaml_path'], 'r') do |f|
123
+ return YAML.load(f)
124
+ end
125
+ end
126
+ end
127
+
128
+ def puts(s = '', width = GameOptions.data['wrap_width'])
129
+ super s.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n") unless s.nil?
130
+ end
131
+
132
+ def print_logo
133
+ puts '/-+-+-+ +-+-+-+-+-+-+-\\'.colorize(:yellow)
134
+ puts '|G|E|M| |W|A|R|R|I|O|R|'.colorize(:yellow)
135
+ puts '\\-+-+-+ +-+-+-+-+-+-+-/'.colorize(:yellow)
136
+ puts '[[[[[[[DEBUGGING]]]]]]]'.colorize(:white) if GameOptions.data['debug_mode']
137
+ end
138
+
139
+ def print_splash_message
140
+ SPLASH_MESSAGE.length.times { print '=' }
141
+ puts
142
+ puts SPLASH_MESSAGE
143
+ SPLASH_MESSAGE.length.times { print '=' }
144
+ puts
145
+ end
146
+
147
+ def print_fortune
148
+ noun1_values = WordList.new('noun-plural')
149
+ noun2_values = WordList.new('noun-plural')
150
+ noun3_values = WordList.new('noun-plural')
151
+
152
+ puts "* Remember: #{noun1_values.get_random_value.colorize(:yellow)} and #{noun2_values.get_random_value.colorize(:yellow)} are the key to #{noun3_values.get_random_value.colorize(:yellow)} *\n\n"
153
+ puts
154
+ end
155
+
156
+ def print_about_text
157
+ puts 'Gem Warrior - A Game of Fortune and Mayhem'.colorize(:yellow)
158
+ puts '=========================================='.colorize(:yellow)
159
+ puts 'Gem Warrior is a text adventure roguelike-lite as a RubyGem created by Michael Chadwick (mike@neb.host) and released as open-source on Github. Take on the task set by Queen Ruby to defeat the evil Emerald and get back the ShinyThing(tm) he stole for terrible, dastardly reasons.'
160
+ puts
161
+ puts 'Explore the land of Jool with the power of text, fighting enemies to improve your station, grabbing curious items that may or may not come in handy, and finally defeating Mr. Emerald himself to win the game.'
162
+ end
163
+
164
+ def print_help_text
165
+ puts 'Gem Warrior - Some Basic Help Commands'.colorize(:yellow)
166
+ puts '======================================'.colorize(:yellow)
167
+ puts '* Basic functions: look, go, character, inventory, attack *'
168
+ puts '* Type \'help\' while in-game for complete list of commands *'
169
+ puts '* Most commands can be abbreviated to their first letter *'
170
+ puts '* Note: if something isn\'t working, try: *'
171
+ puts '* 1) starting a new game *'
172
+ puts '* 2) updating the game *'
173
+ end
174
+
175
+ def display_log_of_attempts
176
+ if File.exist?(GameOptions.data['log_file_path']) and !File.zero?(GameOptions.data['log_file_path'])
177
+ File.open(GameOptions.data['log_file_path']).readlines.each do |line|
178
+ print "#{line}"
179
+ end
180
+ if GameOptions.data['debug_mode']
181
+ print 'Clear log of attempts? (y/n) '
182
+ answer = gets.chomp.downcase
183
+
184
+ case answer
185
+ when 'y', 'yes'
186
+ File.truncate(GameOptions.data['log_file_path'], 0)
187
+ puts 'Log of attempts: erased!'
188
+ end
189
+
190
+ puts
191
+ end
192
+ else
193
+ puts 'No attempts made yet!'
194
+ end
195
+ end
196
+
197
+ def check_for_new_release
198
+ new_release_available = false
199
+ puts 'Checking releases...'
200
+ remote_release = Gems.versions('gemwarrior').first['number']
201
+ local_release = Gemwarrior::VERSION
202
+
203
+ 0.upto(2) do |i|
204
+ if remote_release.split('.')[i].to_i > local_release.split('.')[i].to_i
205
+ new_release_available = true
206
+ end
207
+ end
208
+
209
+ if new_release_available
210
+ puts "GW v#{remote_release} available! Please exit and run 'gem update' before continuing."
211
+ puts
212
+ else
213
+ puts 'You have the latest version. Fantastic!'
214
+ puts
215
+ end
216
+ end
217
+
218
+ def print_options_sound_sytem
219
+ puts
220
+ puts 'Sound System Selection'.colorize(:yellow)
221
+ puts '================================='.colorize(:yellow)
222
+ puts
223
+ win32 = ' (1) WIN32-SOUND '
224
+ OS.windows? ? (print win32) : (print win32.colorize(:light_black))
225
+ print '(SELECTED)'.colorize(:yellow) if GameOptions.data['sound_system'].eql?('win32-sound')
226
+ print "\n"
227
+ print ' (2) FEEP '
228
+ print '(SELECTED)'.colorize(:yellow) if GameOptions.data['sound_system'].eql?('feep')
229
+ print "\n"
230
+ print ' (3) BLOOPS '
231
+ print '(SELECTED)'.colorize(:yellow) if GameOptions.data['sound_system'].eql?('bloops')
232
+ print "\n"
233
+ print "\n"
234
+ print ' (T) TEST SOUND '
235
+ print "\n"
236
+ puts
237
+ puts ' WIN32-SOUND : good quality; Windows-only'
238
+ puts ' FEEP : cross-platform; VERY SLOW and BUGGY'
239
+ puts ' BLOOPS : cross-platform; requires portaudio'
240
+ puts
241
+ puts ' NOTE: none of these are required dependencies anymore, as sound is optional.'
242
+ puts ' If you do not have the one selected installed on your machine, you will get '
243
+ puts ' an error on game load, and no sound will be heard.'
244
+ puts
245
+ puts '================================='.colorize(:yellow)
246
+ puts
247
+ puts 'Enter option number to select sound system, or any other key to exit.'
248
+ puts
249
+ print '[GW_OPTS]-[SOUND_SYSTEM]> '
250
+
251
+ answer = STDIN.getch.chomp.downcase
252
+
253
+ case answer
254
+ when '1'
255
+ if OS.windows?
256
+ puts answer
257
+ GameOptions.add 'sound_system', 'win32-sound'
258
+ else
259
+ puts
260
+ end
261
+ print_options_sound_sytem
262
+ when '2'
263
+ puts answer
264
+ GameOptions.add 'sound_system', 'feep'
265
+ print_options_sound_sytem
266
+ when '3'
267
+ puts answer
268
+ GameOptions.add 'sound_system', 'bloops'
269
+ print_options_sound_sytem
270
+ when 't'
271
+ puts answer
272
+ Audio.init
273
+ play_test_tune
274
+ print_errors
275
+ print_options_sound_sytem
276
+ else
277
+ puts
278
+ return
279
+ end
280
+ end
281
+
282
+ def print_options
283
+ puts
284
+ puts 'Gem Warrior General Options'.colorize(:yellow)
285
+ puts '================================='.colorize(:yellow)
286
+ puts
287
+ puts 'Change several sound options, whether Wordnik is used to generate more dynamic descriptors of entities (valid WORDNIK_API_KEY environment variable must be set), and if attack/fight commands need to have a target or not (if enabled, will attack first monster in vicinty).'
288
+ puts
289
+ puts " (1) SOUND ENABLED : #{GameOptions.data['sound_enabled']}"
290
+ puts " (2) SOUND SYSTEM : #{GameOptions.data['sound_system']}"
291
+ puts " (3) SOUND VOLUME : #{GameOptions.data['sound_volume']}"
292
+ puts " (4) USE WORDNIK : #{GameOptions.data['use_wordnik']}"
293
+ puts " (5) FIGHT COMPLETION : #{GameOptions.data['fight_completion']}"
294
+ puts
295
+ puts '================================='.colorize(:yellow)
296
+ puts
297
+ puts 'Enter option number to change value, or any other key to return to main menu.'
298
+ puts
299
+ print '[GW_OPTS]> '
300
+
301
+ answer = STDIN.getch
302
+
303
+ case answer
304
+ when '1'
305
+ print answer
306
+ GameOptions.data['sound_enabled'] = !GameOptions.data['sound_enabled']
307
+ print_options
308
+ when '2'
309
+ print answer
310
+ print "\n"
311
+ print_options_sound_sytem
312
+ print_options
313
+ when '3'
314
+ print answer
315
+ print "\n"
316
+ print 'Enter a volume from 0.0 to 1.0: '
317
+ new_vol = gets.chomp.to_f.abs
318
+ if new_vol >= 0.0 and new_vol <= 1.0
319
+ GameOptions.data['sound_volume'] = new_vol
320
+ else
321
+ puts 'Not a valid volume.'
322
+ end
323
+ print_options
324
+ when '4'
325
+ print answer
326
+ GameOptions.data['use_wordnik'] = !GameOptions.data['use_wordnik']
327
+ print_options
328
+ when '5'
329
+ print answer
330
+ GameOptions.data['fight_completion'] = !GameOptions.data['fight_completion']
331
+ print_options
332
+ else
333
+ print answer
334
+ return
335
+ end
336
+ end
337
+
338
+ def print_sound_test
339
+ puts
340
+ puts 'Gem Warrior Sound Test'.colorize(:yellow)
341
+ puts '================================='.colorize(:yellow)
342
+
343
+ if GameOptions.data['sound_enabled']
344
+ puts
345
+ puts 'Play all the sounds in Gemwarrior.'
346
+ puts
347
+
348
+ cues = Audio.get_cues
349
+ for cue in cues do
350
+ pp cue[0].to_s
351
+ end
352
+
353
+ puts
354
+ puts '================================='.colorize(:yellow)
355
+ puts
356
+ puts 'Enter sound id to play it, or enter "x" to return to the main menu.'
357
+ else
358
+ GameOptions.data['errors'] = ERROR_SOUND_NOT_ENABLED
359
+ end
360
+
361
+ print_errors
362
+
363
+ puts
364
+ print '[GW_SOUND_TEST]> '
365
+
366
+ answer = gets.chomp.downcase
367
+
368
+ # print answer
369
+ if answer.eql?('x')
370
+ return
371
+ else
372
+ Audio.play_synth(answer.to_sym)
373
+ print_sound_test
374
+ end
375
+ end
376
+
377
+ def print_main_menu
378
+ puts
379
+ puts " GW v#{Gemwarrior::VERSION}"
380
+ puts '======================='
381
+
382
+ save_file = get_save_file_name
383
+ if not save_file.nil?
384
+ recent_name = save_file.instance_variable_get(:@player).name
385
+ puts " #{'(R)esume Game'.colorize(:green)} as #{recent_name.colorize(:yellow)}"
386
+ end
387
+
388
+ puts ' (N)ew Game'
389
+ puts ' (A)bout'
390
+ puts ' (H)elp'
391
+ puts ' (O)ptions'
392
+ puts ' (L)og of Attempts'
393
+ puts ' (S)ound Test'
394
+ puts ' (C)heck for Updates'
395
+ puts ' (E)xit'.colorize(:red)
396
+ puts '======================='
397
+ puts
398
+ end
399
+
400
+ def print_main_menu_prompt
401
+ print '> '
402
+ end
403
+
404
+ def run_main_menu(show_choices = true)
405
+ print_main_menu if show_choices
406
+ print_main_menu_prompt if show_choices
407
+
408
+ choice = STDIN.getch.downcase
409
+
410
+ case choice
411
+ when 'n'
412
+ if overwrite_save?
413
+ clear_screen
414
+ play_intro_tune
415
+ print_splash_message
416
+ print_fortune
417
+ return
418
+ else
419
+ run_main_menu
420
+ end
421
+ when 'r'
422
+ if save_file_exist?
423
+ result = resume_game
424
+ if result.nil?
425
+ run_main_menu
426
+ else
427
+ print_errors
428
+ play_resume_tune
429
+ load_saved_world(result)
430
+ return
431
+ end
432
+ end
433
+ when 'a'
434
+ puts choice
435
+ print_about_text
436
+ run_main_menu
437
+ when 'h'
438
+ puts choice
439
+ print_help_text
440
+ run_main_menu
441
+ when 'o'
442
+ puts choice
443
+ print_options
444
+ run_main_menu
445
+ when 'l'
446
+ puts choice
447
+ display_log_of_attempts
448
+ run_main_menu
449
+ when 's'
450
+ puts choice
451
+ print_sound_test
452
+ run_main_menu
453
+ when 'c'
454
+ puts choice
455
+ check_for_new_release
456
+ run_main_menu
457
+ when 'e', 'x', 'q'
458
+ puts choice
459
+ puts MAIN_MENU_QUIT_MESSAGE
460
+ game.update_options_file
461
+ exit
462
+ when "\c?" # Backspace/Delete
463
+ refresh_menu
464
+ when "\e" # ANSI escape sequence
465
+ case STDIN.getch
466
+ when '[' # CSI
467
+ choice = STDIN.getch
468
+ puts choice
469
+ case choice
470
+ when 'A', 'B', 'C', 'D' # arrow keys
471
+ refresh_menu
472
+ end
473
+ end
474
+ else # All other invalid options
475
+ refresh_menu
476
+ end
477
+ end
478
+
479
+ # need this to handle any non-valid input at the main menu
480
+ def refresh_menu
481
+ clear_screen
482
+ print_logo
483
+ run_main_menu
484
+ end
485
+
486
+ def log_stats(duration, pl)
487
+ # display stats upon exit
488
+ Hr.print('#')
489
+ print 'Gem Warrior'.colorize(color: :white, background: :black)
490
+ print " v#{Gemwarrior::VERSION}".colorize(:yellow)
491
+ print " played for #{duration[:mins].to_s.colorize(color: :white, background: :black)} min(s)"
492
+ print ", #{duration[:secs].to_s.colorize(color: :white, background: :black)} sec(s)"
493
+ # print ", and #{duration[:ms].to_s.colorize(color: :white, background: :black)} ms"
494
+ print "\n"
495
+ Hr.print('-')
496
+ print "#{pl.name.ljust(10).colorize(:green)} "
497
+ print "destroyed #{pl.monsters_killed.to_s.colorize(color: :yellow, background: :black)} monster(s)"
498
+ print "\n".ljust(12)
499
+ print "destroyed #{pl.bosses_killed.to_s.colorize(color: :yellow, background: :black)} boss(es)"
500
+ print "\n".ljust(12)
501
+ print "picked up #{pl.items_taken.to_s.colorize(color: :yellow, background: :black)} item(s)"
502
+ print "\n".ljust(12)
503
+ print "traveled #{pl.movements_made.to_s.colorize(color: :yellow, background: :black)} time(s)"
504
+ print "\n".ljust(12)
505
+ print "rested #{pl.rests_taken.to_s.colorize(color: :yellow, background: :black)} time(s)"
506
+ print "\n".ljust(12)
507
+ print "died #{pl.deaths.to_s.colorize(color: :yellow, background: :black)} time(s)"
508
+ print "\n"
509
+ Hr.print('#')
510
+
511
+ # log stats to file in home directory
512
+ File.open(GameOptions.data['log_file_path'], 'a') do |f|
513
+ f.write "#{Time.now} #{pl.name.rjust(13)} - V:#{Gemwarrior::VERSION} LV:#{pl.level} XP:#{pl.xp} $:#{pl.rox} MK:#{pl.monsters_killed} BK:#{pl.bosses_killed} ITM:#{pl.items_taken} MOV:#{pl.movements_made} RST:#{pl.rests_taken} DTH:#{pl.deaths}\n"
514
+ end
515
+ end
516
+
517
+ def save_game(world)
518
+ mode = GameOptions.data['save_file_mode']
519
+ puts 'Saving game...'
520
+
521
+ if mode.eql? 'Y'
522
+ File.open(GameOptions.data['save_file_yaml_path'], 'w') do |f|
523
+ f.write YAML.dump(world)
524
+ end
525
+ elsif mode.eql? 'M'
526
+ File.open(GameOptions.data['save_file_bin_path'], 'w') do |f|
527
+ f.write Marshal.dump(world)
528
+ end
529
+ else
530
+ puts 'Error: Save file mode not set. Game not saved.'
531
+ return
532
+ end
533
+ puts 'Game saved!'
534
+ end
535
+
536
+ def save_file_exist?
537
+ mode = GameOptions.data['save_file_mode']
538
+ if mode.eql? 'Y'
539
+ File.exist?(GameOptions.data['save_file_yaml_path'])
540
+ elsif mode.eql? 'M'
541
+ File.exist?(GameOptions.data['save_file_bin_path'])
542
+ else
543
+ false
544
+ end
545
+ end
546
+
547
+ def resume_game
548
+ mode = GameOptions.data['save_file_mode']
549
+ puts 'Resuming game...'
550
+
551
+ if mode.eql? 'Y'
552
+ if File.exist?(GameOptions.data['save_file_yaml_path'])
553
+ File.open(GameOptions.data['save_file_yaml_path'], 'r') do |f|
554
+ return YAML.load(f)
555
+ end
556
+ else
557
+ puts 'No save file exists.'
558
+ nil
559
+ end
560
+ elsif mode.eql? 'M'
561
+ if File.exist?(GameOptions.data['save_file_marshal_path'])
562
+ File.open(GameOptions.data['save_file_marshal_path'], 'r') do |f|
563
+ return Marshal.load(f)
564
+ end
565
+ else
566
+ puts 'No save file exists.'
567
+ nil
568
+ end
569
+ end
570
+ end
571
+
572
+ def overwrite_save?
573
+ mode = GameOptions.data['save_file_mode']
574
+ save_file_path = ''
575
+
576
+ if mode.eql? 'Y'
577
+ save_file_path = GameOptions.data['save_file_yaml_path']
578
+ elsif mode.eql? 'M'
579
+ save_file_path = GameOptions.data['save_file_marshal_path']
580
+ end
581
+
582
+ if File.exist?(save_file_path)
583
+ print 'Overwrite existing save file? (y/n) '
584
+ answer = gets.chomp.downcase
585
+
586
+ case answer
587
+ when 'y', 'yes'
588
+ puts 'New game started! Press any key to continue.'
589
+ gets
590
+ return true
591
+ else
592
+ puts 'New game aborted.'
593
+ return false
594
+ end
595
+ end
596
+ true
597
+ end
598
+
599
+ def update_duration(new_duration)
600
+ new_mins = new_duration[:mins]
601
+ new_secs = new_duration[:secs]
602
+ new_ms = new_duration[:ms]
603
+
604
+ world.duration[:mins] += new_mins
605
+ world.duration[:secs] += new_secs
606
+ world.duration[:ms] += new_ms
607
+
608
+ if world.duration[:ms] > 1000
609
+ world.duration[:secs] += world.duration[:ms] / 1000
610
+ world.duration[:ms] = world.duration[:ms] % 1000
611
+ end
612
+
613
+ if world.duration[:secs] > 60
614
+ world.duration[:mins] += world.duration[:secs] / 60
615
+ world.duration[:secs] = world.duration[:secs] % 60
616
+ end
617
+ end
618
+
619
+ def load_saved_world(result)
620
+ self.world = result
621
+ self.evaluator = Evaluator.new(self.world)
622
+ end
623
+
624
+ def setup_screen(initial_command = nil, extra_command = nil, new_skip = false, resume_skip = false)
625
+ # welcome player to game
626
+ clear_screen
627
+ print_logo
628
+
629
+ # main menu loop until new game or exit
630
+ if new_skip
631
+ print_errors
632
+ play_intro_tune
633
+ print_splash_message
634
+ print_fortune
635
+ elsif resume_skip
636
+ result = resume_game
637
+ if result.nil?
638
+ run_main_menu
639
+ else
640
+ print_errors
641
+ load_saved_world(result)
642
+ end
643
+ else
644
+ run_main_menu
645
+ end
646
+
647
+ # hook to do something right off the bat
648
+ puts evaluator.parse(initial_command) unless initial_command.nil?
649
+ puts evaluator.parse(extra_command) unless extra_command.nil?
650
+ end
651
+
652
+ def print_errors
653
+ if GameOptions.data['errors']
654
+ puts "\n\n"
655
+ puts "Errors: #{GameOptions.data['errors'].colorize(:red)}"
656
+ GameOptions.data['errors'] = nil
657
+ end
658
+ end
659
+
660
+ def play_intro_tune
661
+ Audio.play_synth(:intro)
662
+ end
663
+
664
+ def play_resume_tune
665
+ Audio.play_synth(:resume_game)
666
+ end
667
+
668
+ def play_test_tune
669
+ Audio.play_synth(:test)
670
+ end
671
+
672
+ def prompt
673
+ prompt_template = "\n"
674
+ prompt_template += "[LV:%2s][XP:%3s][ROX:%3s][HP:%3s/%-3s] [".colorize(:yellow)
675
+ prompt_template += "%s".colorize(:green)
676
+ prompt_template += " @ ".colorize(:yellow)
677
+ prompt_template += "%s".colorize(:cyan)
678
+ prompt_template += "]".colorize(:yellow)
679
+ prompt_template += "[%s, %s, %s]".colorize(:yellow) if GameOptions.data['debug_mode']
680
+ prompt_template += "\n"
681
+ prompt_template += '[c:character][i:inventory][l:look][u:use][t:take]'
682
+
683
+ prompt_vars_arr = [
684
+ world.player.level,
685
+ world.player.xp,
686
+ world.player.rox,
687
+ world.player.hp_cur,
688
+ world.player.hp_max,
689
+ world.player.name,
690
+ world.location_by_coords(world.player.cur_coords).name_display
691
+ ]
692
+ if GameOptions.data['debug_mode']
693
+ prompt_vars_arr.push(world.player.cur_coords[:x], world.player.cur_coords[:y], world.player.cur_coords[:z])
694
+ end
695
+ print (prompt_template % prompt_vars_arr)
696
+ print "\n"
697
+ end
698
+
699
+ def command_exists?(cmd)
700
+ ENV['PATH'].split(File::PATH_SEPARATOR).collect { |d|
701
+ Dir.entries d if Dir.exist? d
702
+ }.flatten.include?(cmd)
703
+ end
704
+ end
705
+ end