=head1 NAME
Game::UI - UI for the Worm game.
=head1 SYNOPSIS
Nah...
=cut
package Game::UI;
use strict;
use Data::Dumper;
use Carp qw( confess );
use Win32::Console; #Curses/GUI/other?
use Win32::Sound;
use Game::Location;
use Time::HiRes qw( time sleep );
use Game::Application;
=head1 PROPERTIES
=head2 oConsole
Screen console object
=cut
use Class::MethodMaker get_set => [ "oConsole" ];
=head2 oKeyboard
Keyboard console object
=cut
use Class::MethodMaker get_set => [ "oKeyboard" ];
=head2 offsetTop
Where on the screen to display the Lawn.
Default: 0
=cut
use Class::MethodMaker get_set => [ "offsetTop" ];
=head2 offsetLeft
Where on the screen to display the Lawn.
Default: 0
=cut
use Class::MethodMaker get_set => [ "offsetLeft" ];
=head2 oLocationScore
Location for the score output, or undef if none.
=cut
use Class::MethodMaker get_set => [ "oLocationScore" ];
=head2 soundsEnabled
Whether to play sounds or not.
Default: 0
=cut
use Class::MethodMaker get_set => [ "soundsEnabled" ];
=head1 METHODS
=head2 new()
Create new UI.
=cut
sub new { my $pkg = shift;
my $self = {};
bless $self, $pkg;
$self->offsetLeft(0);
$self->offsetTop(0);
$self->oLocationScore(undef);
$self->oConsole(Win32::Console->new(STD_OUTPUT_HANDLE));
$self->oKeyboard(Win32::Console->new(STD_INPUT_HANDLE));
$self->soundsEnabled(0);
return($self);
}
=head2 displayLawn($oLawn)
Initialize the screen and display stuff on the $oLawn
Return 1 on success, else 0.
=cut
sub displayLawn { my $self = shift;
my ($oLawn) = @_;
$self->oConsole->Cls();
for my $row (0 .. ($oLawn->height - 1)) {
$self->oConsole->FillChar('.', $oLawn->width, 0 + $self->offsetLeft, $row + $self->offsetTop);
}
$self->showScore(undef);
return(1);
}
=head2 runMainMenu($oApplication)
Show the menu and accept user input. Run whatever user
action is selected.
Return 1 on success, else 0.
=cut
sub runMainMenu { my $self = shift;
my ($oApplication) = @_;
my %hAction = (
"P" => sub {
$oApplication->runGame();
return(1);
},
"Q" => sub {
$self->oConsole->Cls();
return(0);
},
);
$self->showMainMenu() or return(0);
while(1) {
if(my $rcAction = $hAction{ uc($self->getKeyPressChar()) }) {
$rcAction->() or return(1);
$self->showMainMenu() or return(0);
}
sleep(0.05);
}
return(1);
}
=head2 showMainMenu()
Show the menu on the screen.
Return 1 on success, else 0.
=cut
sub showMainMenu { my $self = shift;
$self->oConsole->Cls();
my $text = qq{
.: MENU :. .: HOW TO PLAY :.
P - Play - F - Turn LEFT
Q - Quit - J - Turn RIGHT
- You are the white worm
Please enlarge the console window before playing
};
print $text;
return(1);
}
=head2 displayObjectAt($oObject)
Display the $oObject at it's Locaton.
Return 1 on success, else 0.
=cut
sub displayObjectAt { my $self = shift;
my ($oObject) = @_;
my $i = 0;
for my $oLocation (@{$oObject->raBodyLocation}) {
my $char = $oObject->raBodyChar->[$i]; #Refactor: Move to worm
$self->displayObjectBodyPartAt($oLocation, $char, $oObject);
$i++;
}
return(1);
}
=head2 displayObjectBodyPartAt($oLocation, [$char = '.'], [$oObject])
Display the $char at the $oLocation. If $oObject is passed,
the style may be influenced by it.
Return 1 on success, else 0.
=cut
my %hColorAttr = (
"white" => $::FG_WHITE,
"gray" => $::FG_GRAY,
"yellow" => $::FG_YELLOW,
"blue" => $::FG_LIGHTBLUE,
"green" => $::FG_LIGHTGREEN,
"red" => $::FG_LIGHTRED,
);
sub displayObjectBodyPartAt { my $self = shift;
my ($oLocation, $char, $oObject) = @_;
$char ||= ".";
my $attr = $hColorAttr{"gray"};
$oObject and $attr = $hColorAttr{ $oObject->color() || "gray" };
# $char eq '$' and $attr = $hColorAttr{"yellow"};
my ($left, $top) = ($oLocation->left + $self->offsetLeft, $oLocation->top + $self->offsetTop);
$self->oConsole->FillAttr($attr, 1, $left, $top);
$self->oConsole->FillChar($char, 1, $left, $top);
return(1);
}
=head2 getUserAction()
Return logical user action:
"turn left"
"turn right"
"quit"
"pause"
Return "" if there is no user input pending.
=cut
my $rhKeyInput = {
"f" => "turn left",
"j" => "turn right",
"q" => "quit",
"p" => "pause",
};
sub getUserAction { my $self = shift;
my $char = $self->getKeyPressChar() or return("");
$char = lc($char);
return( $rhKeyInput->{$char} || "" );
}
=head2 showScore([$score])
If there is a Location for the score, display it.
Return 1 on success, else 0.
=cut
sub showScore { my $self = shift;
my ($score) = @_;
defined($score) or $score = "";
$self->oLocationScore or return(1);
$self->oConsole->WriteChar("Score: $score", $self->oLocationScore->left, $self->oLocationScore->top); #Or nothing
return(1);
}
=head2 prizeWasClaimedBy($oPrize, $oObject)
The $oPrize was claimed by $oObject. Note this by playing a
sound (if available).
Return 1 on success, else 0.
=cut
sub prizeWasClaimedBy { my $self = shift;
my ($oPrize, $oObject) = @_;
$self->playSound("prize");
return(1);
}
=head2 wormHasCrashed($oObject)
The worm $oObject has crashed.
Note this by playing a sound (if available).
Return 1 on success, else 0.
=cut
sub wormHasCrashed { my $self = shift;
my ($oObject) = @_;
$self->playSound("crash");
return(1);
}
=head2 playSound($name)
If soundsEnabled(), play the sound $name if it's available.
Return 1 on success, else 0.
=cut
sub playSound { my $self = shift;
my ($name) = @_;
$self->soundsEnabled or return(1);
my $file = "resource/sound/$name.wav";
-r $file or return(0);
Win32::Sound::Volume('40%');
Win32::Sound::Play($file, SND_ASYNC | SND_NODEFAULT);
return(1);
}
=head2 getKeyPressChar()
Check for keyboard key presses, and return the char of the
key pressed, or undef if no key was pressed (don't block).
=cut
sub getKeyPressChar { my $self = shift;
$self->oKeyboard->GetEvents() or return(undef); #Skip it if there is no input (it will block otherwise)
my ($eventType, $keyDown, $repeat, $keycode, $scancode, $char, $control) = $self->oKeyboard->Input();
$eventType and $eventType == 1 and $keyDown and $char or return(undef);
return( chr($char) );
}
1;
#EOF