437 lines
13 KiB
Perl
437 lines
13 KiB
Perl
package GEmeTool::View::PokemonPCWindow;
|
|
|
|
use v5.16.3;
|
|
|
|
use strict;
|
|
use warnings;
|
|
use utf8;
|
|
|
|
use Cairo::GObject;
|
|
use Glib;
|
|
use Glib::Object::Introspection;
|
|
use Glib::IO;
|
|
use Path::Tiny;
|
|
use Math::Trig;
|
|
use Rsaves;
|
|
|
|
use Moo;
|
|
|
|
use GEmeTool::View::PokemonEditorWindow;
|
|
|
|
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,
|
|
);
|
|
|
|
has _win => (
|
|
is => 'rw',
|
|
);
|
|
|
|
has _current_box => ( is => 'rw', );
|
|
|
|
my $BOX_X = 275;
|
|
my $BOX_Y = 50;
|
|
my $BOX_WIDTH = 312;
|
|
my $BOX_HEIGHT = 340;
|
|
|
|
my $ARROW_X = 28;
|
|
my $ARROW_Y = 6;
|
|
my $ARROW_WIDTH = 16;
|
|
my $ARROW_HEIGHT = 32;
|
|
my ($CLOSE_BOX_X, $CLOSE_BOX_Y, $CLOSE_BOX_WIDTH, $CLOSE_BOX_HEIGHT) = (420, 0, 180, 40);
|
|
|
|
has _cursor_position => ( is => 'rw' );
|
|
|
|
my $root = path(__FILE__)->parent->parent->parent->parent;
|
|
|
|
sub start {
|
|
my $self = shift;
|
|
my $gtk_window = Gtk4::Window->new;
|
|
$self->_win($gtk_window);
|
|
my $controller_motion = Gtk4::EventControllerMotion->new;
|
|
my $controller_gesture = Gtk4::GestureClick->new;
|
|
my $save = $self->_save;
|
|
my $pc = $save->get_pc_system;
|
|
my $box = $pc->get_box(0);
|
|
my $headerbar = Gtk4::HeaderBar->new;
|
|
$headerbar->set_decoration_layout('');
|
|
my $title_grid = Gtk4::Grid->new;
|
|
$title_grid->attach(Gtk4::Label->new('Pokémon Storage System'), 0, 0, 12, 1);
|
|
my $save_button = Gtk4::Button->new_with_label('Save');
|
|
$save_button->signal_connect(clicked => sub {
|
|
$self->_save->get_pc_system->save;
|
|
});
|
|
$title_grid->attach($save_button, 0, 1, 2, 1);
|
|
$headerbar->set_title_widget($title_grid);
|
|
$gtk_window->set_titlebar($headerbar);
|
|
$controller_gesture->set_button(1);
|
|
$self->_current_box($box);
|
|
$controller_gesture->signal_connect(
|
|
pressed => sub {
|
|
$self->_on_primary_click(@_);
|
|
}
|
|
);
|
|
$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);
|
|
$canvas->add_controller($controller_gesture);
|
|
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;
|
|
|
|
my $selected_pokemon = $self->get_selected_pokemon;
|
|
$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);
|
|
$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_pokemon_details( $cairo, $selected_pokemon );
|
|
$self->draw_not_implemented($cairo);
|
|
$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_not_implemented {
|
|
my $self = shift;
|
|
my $cairo = shift;
|
|
$cairo->rectangle(200, 0, 180, 40);
|
|
$cairo->set_source_rgb(0, 0, 0);
|
|
$cairo->fill;
|
|
$cairo->set_source_rgb(1, 1, 1);
|
|
$cairo->set_font_size(20);
|
|
$cairo->move_to( 200, 30);
|
|
$cairo->text_path("Not implemented");
|
|
$cairo->fill;
|
|
}
|
|
|
|
|
|
sub _on_primary_click {
|
|
my $self = shift;
|
|
my ( $gesture, $n_press, $x, $y ) = @_;
|
|
return if $self->_check_click_next_prev_box( $gesture, $n_press, $x, $y );
|
|
return if $self->_check_click_close_box($gesture, $n_press, $x, $y);
|
|
return if $self->_check_selected_pokemon;
|
|
}
|
|
|
|
sub _check_selected_pokemon {
|
|
my $self = shift;
|
|
if (defined $self->get_selected_pokemon) {
|
|
my $editor = GEmeTool::View::PokemonEditorWindow->new(pokemon => $self->get_selected_pokemon);
|
|
$editor->start;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
sub _check_click_close_box {
|
|
my $self = shift;
|
|
my ( $gesture, $n_press, $x, $y ) = @_;
|
|
my $match_x = $x >= $CLOSE_BOX_X && $x <= $CLOSE_BOX_WIDTH + $CLOSE_BOX_X;
|
|
my $match_y = $y >= $CLOSE_BOX_Y && $y <= $CLOSE_BOX_HEIGHT + $CLOSE_BOX_Y;
|
|
if ($match_x && $match_y) {
|
|
$self->_win->close;
|
|
return 1;
|
|
}
|
|
return;
|
|
}
|
|
|
|
sub _check_click_next_prev_box {
|
|
my $self = shift;
|
|
my ( $gesture, $n_press, $x, $y ) = @_;
|
|
|
|
return if $x < $BOX_X;
|
|
return if $y < $BOX_Y;
|
|
|
|
$x -= $BOX_X;
|
|
$y -= $BOX_Y;
|
|
|
|
return if $x >= $BOX_WIDTH;
|
|
return if $y >= $BOX_HEIGHT;
|
|
|
|
my $x_matches =
|
|
$x >= $BOX_WIDTH - $ARROW_X && $x <= $BOX_WIDTH - $ARROW_X + $ARROW_WIDTH;
|
|
my $y_matches = $y >= $ARROW_Y && $y <= $ARROW_Y + $ARROW_HEIGHT;
|
|
if ( $x_matches && $y_matches ) {
|
|
my $box = $self->_current_box;
|
|
my $save = $self->_save;
|
|
my $pc = $save->get_pc_system;
|
|
if ( $box->number == 13 ) {
|
|
$self->_current_box( $pc->get_box(0) );
|
|
return 1;
|
|
}
|
|
$self->_current_box( $pc->get_box( $box->number + 1 ) );
|
|
return 1;
|
|
}
|
|
$x_matches = $x >= $ARROW_X - $ARROW_WIDTH && $x <= $ARROW_X;
|
|
$y_matches = $y >= $ARROW_Y && $y <= $ARROW_Y + $ARROW_HEIGHT;
|
|
if ( $x_matches && $y_matches ) {
|
|
my $box = $self->_current_box;
|
|
my $save = $self->_save;
|
|
my $pc = $save->get_pc_system;
|
|
if ( $box->number == 0 ) {
|
|
$self->_current_box( $pc->get_box(13) );
|
|
return 1;
|
|
}
|
|
$self->_current_box( $pc->get_box( $box->number - 1 ) );
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
sub draw_pokemon_details {
|
|
my $self = shift;
|
|
my $cairo = shift;
|
|
my $selected_pokemon = shift;
|
|
return if !defined $selected_pokemon;
|
|
return if $selected_pokemon->species == 0;
|
|
my $output_image = Cairo::ImageSurface->create( 'argb32', 64, 64 );
|
|
my $image_context = Cairo::Context->create($output_image);
|
|
my $surface = Cairo::ImageSurface->create_from_png(
|
|
$root->child( $selected_pokemon->get_front ) );
|
|
my $scale_factor = 2;
|
|
$cairo->scale( ($scale_factor) x 2 );
|
|
$image_context->set_source_surface( $surface, 0, 0 );
|
|
$image_context->get_source()->set_filter('nearest');
|
|
$image_context->paint;
|
|
$cairo->set_source_surface( $output_image, 20, 30 );
|
|
$cairo->get_source()->set_filter('nearest');
|
|
$cairo->paint;
|
|
|
|
my $pokemon_species = $Rsaves::Constants::Emerald::Species::SPECIES[$selected_pokemon->species];
|
|
$cairo->select_font_face( 'Sans', 'normal', 'normal' );
|
|
$cairo->set_source_rgb( 1, 1, 1 );
|
|
$cairo->set_font_size(8);
|
|
$cairo->move_to( 5, 120 );
|
|
$cairo->text_path(Rsaves::translate_3rd_encoding($selected_pokemon->nickname));
|
|
$cairo->fill;
|
|
|
|
$cairo->move_to( 5, 128 );
|
|
$cairo->text_path('/'.$pokemon_species);
|
|
$cairo->set_source_rgb( 1, 1, 1 );
|
|
$cairo->fill;
|
|
# $cairo->set_source_rgb( 0, 0, 0 );
|
|
# $cairo->set_line_width(0.8);
|
|
# $cairo->stroke;
|
|
$cairo->move_to( 6, 140 );
|
|
my $gender = $selected_pokemon->gender;
|
|
$cairo->set_font_size(15);
|
|
if ($gender == 0) {
|
|
$cairo->text_path("♂");
|
|
$cairo->set_source_rgb( 0, 0, 1 );
|
|
$cairo->fill;
|
|
}
|
|
if ($gender == 1) {
|
|
$cairo->text_path("♀");
|
|
$cairo->set_source_rgb( 1, 0, 0 );
|
|
$cairo->fill;
|
|
}
|
|
$cairo->set_source_rgb( 1, 1, 1 );
|
|
$cairo->set_font_size(8);
|
|
$cairo->move_to( 18, 138 );
|
|
$cairo->text_path("Lv@{[$selected_pokemon->level]}");
|
|
$cairo->set_source_rgb( 1, 1, 1 );
|
|
$cairo->fill;
|
|
|
|
$cairo->scale( ( 1 / $scale_factor ) x 2 );
|
|
|
|
}
|
|
|
|
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 = $self->_current_box;
|
|
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 = $self->_current_box;
|
|
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_icon );
|
|
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 = $self->_current_box;
|
|
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;
|
|
$surface = Cairo::ImageSurface->create_from_png(
|
|
$root->child('resources/right_arrow_pc.png') );
|
|
$cairo->set_source_surface(
|
|
$surface,
|
|
$BOX_WIDTH / 2 - $ARROW_X / 2,
|
|
$ARROW_Y / 2
|
|
);
|
|
$cairo->get_source()->set_filter('nearest');
|
|
$cairo->paint;
|
|
$cairo->scale( -1, 1 );
|
|
$cairo->set_source_surface( $surface, -$ARROW_X / 2, $ARROW_Y / 2 );
|
|
$cairo->get_source()->set_filter('nearest');
|
|
$cairo->paint;
|
|
$cairo->scale( -1, 1 );
|
|
$cairo->scale( 1 / 2, 1 / 2 );
|
|
|
|
}
|
|
1;
|