package Hexed::Fixed; use strict; use warnings; use include; use Hexed::Util; use Curses; # Set up the window and grid. sub init { my ($class, $dat) = @_; # Set up our tile grid. foreach my $y (0 .. $dat->{Height}) { foreach my $x (0 .. $dat->{Width}) { $dat->{Grid}[$y][$x] = { char => " ", # What character? color => 0, # What color? Numeric attrib value. fg => "", # Foreground color bg => "", # Background color draw => 1 # Do we need to redraw this one? }; } } delete $dat->{Opts}; return bless $dat, $class; } # Modify a tile. sub mod { my ($self, $y, $x, $new) = @_; my $tile = $self->{Grid}[$y][$x]; # Accepts a hash with any of the following {char}, {color}, {fg}, or {bg}. die "Gimme a hash of tile stuff!\n" unless ref $new eq "HASH"; if (my $color = delete $new->{color}) { if ($color eq "/") { # Standend ($tile->{fg}, $tile->{bg}) = ("grey", "black"); } else { ($tile->{fg}, $tile->{bg}) = split("/", $color); } } %$tile = (%$tile, %$new); $tile->{color} = paint $tile->{fg}, $tile->{bg}; $tile->{draw} = 1; return $tile->{char}; } # Edit a single line of the display. sub edit { my ($self, $y, $x, $msg) = @_; ($msg) = wrap(-1, $msg); $y = center(1, $self->{Height}) if $y eq "center"; my $stripped = strip $msg; $x = center(length($stripped), $self->{Width}) if $x eq "center"; # Does it fit? if ($y < 0 or $x < 0 or $y > $self->{Height}) { die "Message can't fit!\n"; } elsif ($x + length($stripped) > $self->{Width}) { # Fit as many full words as possible. my $cut = ""; my $scut = $cut; $x = 0; while (length($scut) <= $self->{Width}) { $msg =~ s/^([^ ]+) //; my $add = $1; $add = " $add" if $cut; my $sadd = strip $add; $scut = strip $cut; last if length($scut) + length($sadd) > $self->{Width}; $cut .= $add; } $msg = $cut; } # Split by formatting tags. my $color = "grey"; foreach my $string (split(/(<[^>]+>)/, $msg)) { next unless $string; if ($string =~ m/<([^>]+)/) { $color = $1; } else { # A normal part of the string. foreach my $char (split //, $string) { $self->mod($y, $x++, { char => $char, color => $color }); } } } return $msg; } # Edit a portion of the display. sub place { my ($self, $y, $x, $pic, $color) = (@_, ""); # Format the pic. my $new = []; my $long = 0; chomp($pic); # Using heredocs leaves an extra newline foreach my $line (split(/\n/, $pic)) { $long = length($line) if length($line) > $long; push @$new, []; foreach my $char (split(//, $line)) { push @{ $new->[-1] }, $char; } } # Now pad it. foreach my $y (0 .. $#{ $new }) { push @{ $new->[$y] }, " " until $long == scalar @{ $new->[$y] }; } # Where does it go? $y = center(scalar @$new, $self->{Height}) if $y eq "center"; $x = center($long, $self->{Width}) if $x eq "center"; die "Attempt to place a picture at $y, $x bounded off!\n" if $y < 0 or $x < 0 or $y + $#{ $new } > $self->{Height} or $x + $long > $self->{Width}; # Merge the grids. foreach my $Y (0 .. $#{ $new }) { foreach my $X (0 .. $#{ $new->[$Y] }) { $self->mod($y + $Y, $x + $X, { char => $new->[$Y][$X], color => $color }); } } return ($y, $x); } # Redraw the window. sub draw { my $self = shift; my $force = shift; # After HexedUI does fullscreen nonsense, we have to # literally redraw everything. # Clear it... $self->{Win}->standend; my $off = $self->{Off}; foreach my $y (0 .. $self->{Height}) { my $prev = ""; foreach my $x (0 .. $self->{Width}) { # Do we need to redraw this tile? my $tile = $self->{Grid}[$y][$x]; if ($tile->{color} ne $prev) { # Change our color! $self->{Win}->standend; $self->{Win}->attron($tile->{color}) if $tile->{color}; $prev = $tile->{color}; } next unless $tile->{draw} or $force; $tile->{draw} = 0; # Factor in offset. $self->{Win}->addch($y + ($off/2), $x + ($off/2), $tile->{char}); } } $self->{Win}->refresh; $self->{Win}->standend; return 1; } # Erase the grid and clear the display. sub cls { my $self = shift; # Blank me! foreach my $row (@{ $self->{Grid} }) { foreach (@$row) { $_->{char} = " "; $_->{fg} = $_->{bg} = ""; $_->{draw} = $_->{color} = 0; } } # No point in calling mod() frequently and then doing a draw() when we can # just tell Curses to clear the window. (And reborder it if needed.) $self->{Win}->standend; $self->{Win}->erase; $self->{Win}->box(0, 0) if $self->{Border}; return 1; } # Sets a background color for the entire window. sub bg { my $self = shift; my $color = shift; my ($y1, $x1, $y2, $x2) = (@_) ? @_ : (0, 0, $self->{Height}, $self->{Width}); ($y1, $y2) = ($y2, $y2) if $y1 > $y2; ($x1, $x2) = ($x2, $x2) if $x1 > $x2; foreach my $y ($y1 .. $y2) { foreach my $x ($x1 .. $x2) { $self->mod($y, $x, { bg => $color }); } } return $color; } 42;