diff --git a/Build.PL b/Build.PL index c856bc3..c9026cd 100755 --- a/Build.PL +++ b/Build.PL @@ -18,6 +18,7 @@ my $build = Module::Build->new( 'namespace::clean' => 0, 'Cairo::GObject' => 0, 'UUID::URandom' => 0, + 'Glib' => 0, }, ); $build->create_build_script; diff --git a/lib/GEmeTool/Save.pm b/lib/GEmeTool/Save.pm index fde6fe0..bce7869 100644 --- a/lib/GEmeTool/Save.pm +++ b/lib/GEmeTool/Save.pm @@ -27,6 +27,8 @@ has _initial_rematches => ( is => 'rw' ); has _initial_flags => ( is => 'rw' ); +has _pc => ( is => 'rw' ); + sub instance { my $class = shift; my $input_file = shift; @@ -221,10 +223,15 @@ sub _get_flags_hash_by_id { sub get_pc_system { my $self = shift; + if (defined $self->_pc) { + return $self->_pc; + } my $pc = Rsaves::read_pc_storage($self->_get_current_save); - return GEmeTool::Save::PokemonPC->new(_pc => $pc, _save => sub { + my $return = GEmeTool::Save::PokemonPC->new(_pc => $pc, _save => sub { my $pc = shift; Rsaves::save_pc_changes($self->_get_current_save, $pc) }); + $self->_pc($return); + return $return; } 1; diff --git a/lib/GEmeTool/Save/PokemonBox.pm b/lib/GEmeTool/Save/PokemonBox.pm index 1e27714..57d7170 100644 --- a/lib/GEmeTool/Save/PokemonBox.pm +++ b/lib/GEmeTool/Save/PokemonBox.pm @@ -24,6 +24,10 @@ has _box => ( required => 1, ); +has _pokemons => ( + is => 'rw', +); + sub wallpaper { my $self = shift; my $arg = shift; @@ -45,9 +49,19 @@ sub name { sub get_pokemon { my $self = shift; my $number = shift; + my $pokemons = $self->_pokemons; + if (!defined $pokemons) { + $pokemons = []; + $self->_pokemons($pokemons); + } if ($number < 0 || $number > 29) { die "Pokemon boxes can only hold pokemon from 0 to 29, index $number invalid."; } - return GEmeTool::Save::Pokemon->new( _pokemon => $self->_box->[$number] ); + if (defined $pokemons->[$number]) { + return $pokemons->[$number]; + } + my $return = GEmeTool::Save::Pokemon->new( _pokemon => $self->_box->[$number] ); + $pokemons->[$number] = $return; + return $return; } 1; diff --git a/lib/GEmeTool/Save/PokemonPC.pm b/lib/GEmeTool/Save/PokemonPC.pm index 4836c5e..2191189 100644 --- a/lib/GEmeTool/Save/PokemonPC.pm +++ b/lib/GEmeTool/Save/PokemonPC.pm @@ -20,6 +20,10 @@ has _save => ( required => 1, ); +has _boxes => ( + is => 'rw', +); + sub boxes { my $self = shift; my $pc = $self->_pc; @@ -30,12 +34,21 @@ sub boxes { return \@boxes; } + sub get_box { my $self = shift; my $number = shift; + my $boxes = $self->_boxes; + if (!defined $boxes) { + $self->_boxes([]); + $boxes = $self->_boxes; + } if ($number < 0 || $number > 13) { die "Bad box number $number."; } + if (defined $boxes->[$number]) { + return $boxes->[$number]; + } my $pc = $self->_pc; my $wallpaper = \$pc->{wallpapers}[$number]; my $name = \$pc->{boxes_names}[$number]; @@ -45,6 +58,7 @@ sub get_box { _name => $name, _box => $box ); + $boxes->[$number] = $return; return $return; } diff --git a/lib/GEmeTool/View/MainWindow.pm b/lib/GEmeTool/View/MainWindow.pm index eab1133..3032148 100644 --- a/lib/GEmeTool/View/MainWindow.pm +++ b/lib/GEmeTool/View/MainWindow.pm @@ -11,10 +11,11 @@ use Glib::IO; use Glib::Object::Introspection; use Data::Dumper; use Path::Tiny; + use GEmeTool::Options; use GEmeTool::Save; use GEmeTool::View::LogWindow; -use Cairo::GObject; +use GEmeTool::View::PokemonPCWindow; use Moo; @@ -219,11 +220,11 @@ sub open_file { } sub start_editing_file { - my $self = shift; - my $file = $self->_file; - my $save_as_action = $self->_save_as_action; - my $pc_action = $self->_pc_action; - my $box_app = Gtk4::Box->new( 'vertical', 0 ); + my $self = shift; + my $file = $self->_file; + my $save_as_action = $self->_save_as_action; + my $pc_action = $self->_pc_action; + my $box_app = Gtk4::Box->new( 'vertical', 0 ); $self->_save( GEmeTool::Save->instance($file) ); my $save_obj = $self->_save; @@ -324,82 +325,7 @@ sub activate_about { my $root = path(__FILE__)->parent->parent->parent->parent; sub activate_view_pc { - my $self = shift; - my $gtk_window = Gtk4::Window->new; - my $canvas = Gtk4::DrawingArea->new; - $gtk_window->set_default_size( 312, 340 ); - $gtk_window->set_resizable(0); - $canvas->set_draw_func( - sub { - my $canvas = shift; - my $cairo = shift; - my $width = shift; - my $height = shift; - - $self->draw_box($cairo); - $self->draw_pokemon($cairo); - $self->draw_box_name($cairo); - } - ); - $gtk_window->set_child($canvas); - $gtk_window->present; -} - -sub draw_box_name { - my $self = shift; - my $cairo = shift; - my $save = $self->_save; - my $pc = $save->get_pc_system; - my $box = $pc->get_box(0); - my $name = Rsaves::translate_3rd_encoding($box->name); - $cairo->scale( 1, 1 ); - $cairo->set_source_rgb(0, 0, 0); - $cairo->select_font_face('monospace', 'normal', 'normal'); - $cairo->set_font_size(20); - $cairo->move_to(75 - (length($name) * 5), 19); - $cairo->show_text($name); -} - -sub draw_pokemon { - my $self = shift; - my $cairo = shift; - my $save = $self->_save; - my $pc = $save->get_pc_system; - my $box = $pc->get_box(0); - $cairo->scale(0.95, 0.95); - for ( my $i = 0 ; $i < 5 ; $i++ ) { - # ROW - for ( my $j = 0 ; $j < 6 ; $j++ ) { - # COLUMN - my $pokemon = $box->get_pokemon($i*6 + $j); - my $pokemon_image = - $root->child($pokemon->get_image); - my $surface = Cairo::ImageSurface->create_from_png($pokemon_image); - my $output_image = Cairo::ImageSurface->create( 'argb32', 32, 32 ); - my $image_context = Cairo::Context->create($output_image); - $image_context->set_source_surface( $surface, 0, 0 ); - $image_context->get_source()->set_filter('nearest'); - $image_context->paint; - my $p = 25; - $cairo->set_source_surface( $output_image, $j * ($p + 1), - 20 + ( ( $p - 3 ) * $i ) ); - $cairo->get_source()->set_filter('nearest'); - $cairo->paint; - } - } -} - -sub draw_box { - my $self = shift; - my $cairo = shift; - my $surface = Cairo::ImageSurface->create_from_png( - $root->child('resources/forest.png') ); - - $cairo->scale( 2, 2 ); - $cairo->set_source_surface( $surface, 0, 0 ); - $cairo->get_source()->set_filter('nearest'); - - $cairo->paint; - + my $self = shift; + GEmeTool::View::PokemonPCWindow->new( _save => $self->_save )->start; } 1; diff --git a/lib/GEmeTool/View/PokemonPCWindow.pm b/lib/GEmeTool/View/PokemonPCWindow.pm new file mode 100644 index 0000000..1fac2ef --- /dev/null +++ b/lib/GEmeTool/View/PokemonPCWindow.pm @@ -0,0 +1,234 @@ +package GEmeTool::View::PokemonPCWindow; + +use v5.16.3; + +use strict; +use warnings; + +use Cairo::GObject; +use Glib; +use Glib::IO; +use Path::Tiny; +use Math::Trig; + +use Moo; + +Glib::Object::Introspection->setup( + basename => 'Gtk', + version => '4.0', + package => 'Gtk4', +); + +Glib::Object::Introspection->setup( + basename => 'Gdk', + version => '4.0', + package => 'Gdk', +); + +has _save => ( + is => 'ro', + required => 1, +); + + +my $BOX_X = 275; +my $BOX_Y = 50; +my $BOX_WIDTH = 312; +my $BOX_HEIGHT = 340; + +has _cursor_position => ( is => 'rw' ); + +my $root = path(__FILE__)->parent->parent->parent->parent; + +sub start { + my $self = shift; + my $gtk_window = Gtk4::Window->new; + my $controller_motion = Gtk4::EventControllerMotion->new; + $controller_motion->signal_connect( + motion => sub { + my ( $x, $y ) = @_[ 1, 2 ]; + $self->_cursor_position( [ $x, $y ] ); + } + ); + my $canvas = Gtk4::DrawingArea->new; + $canvas->add_controller($controller_motion); + my $scale_factor_pc = 2.5; + my ($width, $height) = map { $_ * $scale_factor_pc } (256, 158); + $gtk_window->set_default_size( $width, $height ); + $gtk_window->set_resizable(0); + $canvas->set_draw_func( + sub { + my $canvas = shift; + my $cairo = shift; + my $width = shift; + my $height = shift; + + $self->draw_pc($cairo, $scale_factor_pc); + { + my $output_box = Cairo::ImageSurface->create( 'argb32', $BOX_WIDTH, $BOX_HEIGHT ); + my $box_context = Cairo::Context->create($output_box); + $self->draw_box($box_context); + my $selected_pokemon = $self->get_selected_pokemon; + $self->draw_pokemon( $box_context, $selected_pokemon ); + $self->draw_box_name($box_context); + $cairo->set_source_surface( $output_box, $BOX_X, $BOX_Y ); + $cairo->paint; + } + $self->draw_cursor($cairo); + } + ); + $gtk_window->set_child($canvas); + $gtk_window->present; + Glib::Timeout->add( + 1000 / 15, + sub { + $canvas->queue_draw; + return 1; + } + ); +} + +sub draw_pc { + my $self = shift; + my $cairo = shift; + my $scale_factor = shift; + $cairo->scale( ($scale_factor) x 2 ); + my $surface = Cairo::ImageSurface->create_from_png($root->child('resources/pc_background.png')); + $cairo->set_source_surface( $surface, 0, 0 ); + $cairo->get_source()->set_filter('nearest'); + $cairo->paint; + $cairo->scale( ( 1 / $scale_factor ) x 2 ); +} + +sub draw_cursor { + my $self = shift; + my $cairo = shift; + my $cursor_png = $root->child('resources/hand_cursor.png'); + my $surface = Cairo::ImageSurface->create_from_png($cursor_png); + my $output_image = Cairo::ImageSurface->create( 'argb32', 32, 32 ); + my $image_context = Cairo::Context->create($output_image); + my $cursor_position = $self->_cursor_position; + return if !defined $cursor_position; + my $scale_factor = 0.95 * 2; + $cairo->scale( ($scale_factor) x 2 ); + my ( $x, $y ) = map { $_ / $scale_factor } @$cursor_position; + $x -= 10; + $y -= 20; + $image_context->set_source_surface( $surface, 0, 0 ); + $image_context->get_source()->set_filter('nearest'); + $image_context->paint; + my $p = 25; + $cairo->set_source_surface( $output_image, $x, $y, ); + $cairo->get_source()->set_filter('nearest'); + $cairo->paint; + $cairo->scale( ( 1 / $scale_factor ) x 2 ); +} + +sub draw_box_name { + my $self = shift; + my $cairo = shift; + my $save = $self->_save; + my $pc = $save->get_pc_system; + my $box = $pc->get_box(0); + my $name = Rsaves::translate_3rd_encoding( $box->name ); + my $scale_factor = 0.95 * 2; + $cairo->scale( $scale_factor, $scale_factor ); + $cairo->set_source_rgb( 0, 0, 0 ); + $cairo->select_font_face( 'monospace', 'normal', 'normal' ); + $cairo->set_font_size(20); + $cairo->move_to( 75 - ( length($name) * 5 ), 19 ); + $cairo->show_text($name); + $cairo->scale( ( 1 / $scale_factor ) x 2 ); +} + +sub draw_pokemon { + my $self = shift; + my $cairo = shift; + my $selected_pokemon = shift; + my $save = $self->_save; + my $pc = $save->get_pc_system; + my $box = $pc->get_box(0); + my $scale_factor = 0.95 * 2; + $cairo->scale( $scale_factor, $scale_factor ); + + for ( my $i = 0 ; $i < 5 ; $i++ ) { + + # ROW + for ( my $j = 0 ; $j < 6 ; $j++ ) { + + # COLUMN + my $pokemon = $box->get_pokemon( $i * 6 + $j ); + my $is_selected = + defined $selected_pokemon && $pokemon eq $selected_pokemon; + my $pokemon_image = $root->child( $pokemon->get_image ); + my $p = 25; + my $x = $j * ( $p + 1 ); + my $y = 20 + ( ( $p - 3 ) * $i ); + + if ($is_selected) { + $cairo->set_source_rgb( 1, 1, 0 ); + $cairo->arc( $x + 5 + 11, $y + 10 + 11, 22/2, 0, pi*2 ); + $cairo->fill; + } + + my $surface = Cairo::ImageSurface->create_from_png($pokemon_image); + my $output_image = Cairo::ImageSurface->create( 'argb32', 32, 32 ); + my $image_context = Cairo::Context->create($output_image); + $image_context->set_source_surface( $surface, 0, 0 ); + $image_context->get_source()->set_filter('nearest'); + $image_context->paint; + $cairo->set_source_surface( $output_image, $x, $y ); + $cairo->get_source()->set_filter('nearest'); + $cairo->paint; + } + } + $cairo->scale( ( 1 / $scale_factor ) x 2 ); +} + +sub get_selected_pokemon { + my $self = shift; + my $cursor_position = $self->_cursor_position; + return if !defined $cursor_position; + my ($x, $y) = @$cursor_position; + + return if $x < $BOX_X; + return if $y < $BOX_Y; + + $x -= $BOX_X; + $y -= $BOX_Y; + + return if $x >= $BOX_WIDTH - 13; + return if $y >= $BOX_HEIGHT; + + my $scale_factor = 0.95 * 2; + ( $x, $y ) = map { $_ / $scale_factor } ($x, $y); + return if $y <= 20; + $y -= 20; + + my $p = 25; + my $i = int( $y / ( $p - 3 ) ); + my $j = int( $x / ( $p + 1 ) ); + my $save = $self->_save; + my $pc = $save->get_pc_system; + my $box = $pc->get_box(0); + my $number = $i * 6 + $j; + return if $number > 29; + my $pokemon = $box->get_pokemon($number); + return $pokemon; +} + +sub draw_box { + my $self = shift; + my $cairo = shift; + my $surface = Cairo::ImageSurface->create_from_png( + $root->child('resources/forest.png') ); + + $cairo->scale( 2, 2 ); + $cairo->set_source_surface( $surface, 0, 0 ); + $cairo->get_source()->set_filter('nearest'); + + $cairo->paint; + $cairo->scale( 1 / 2, 1 / 2 ); + +} +1; diff --git a/resources/hand_cursor.png b/resources/hand_cursor.png new file mode 100644 index 0000000..1a87e68 Binary files /dev/null and b/resources/hand_cursor.png differ diff --git a/resources/pc_background.png b/resources/pc_background.png new file mode 100644 index 0000000..e47e082 Binary files /dev/null and b/resources/pc_background.png differ