Adding ev and iv support.

This commit is contained in:
sergiotarxz 2024-03-11 02:50:43 +01:00
parent d7da1100fe
commit e2743b9dc6
2 changed files with 335 additions and 117 deletions

View File

@ -13,33 +13,117 @@ use Rsaves::Constants::Emerald::SpeciesData;
has _pokemon => ( is => 'rw', );
sub species {
my $self = shift;
my $arg = shift;
my $pokemon = $self->_pokemon;
my $self = shift;
my $arg = shift;
my $pokemon = $self->_pokemon;
my $substruct_0 = $pokemon->{substructures}[0];
if (defined $arg) {
if ( defined $arg ) {
$substruct_0->{species} = $arg;
}
return $substruct_0->{species};
}
sub ivs {
my $self = shift;
my $arg = shift;
my $current_value =
$self->_pokemon->{substructures}[3]{ivs_egg_status_and_ability};
my @stats = (
'HP', 'Attack',
'Defense', 'Speed',
'Special Attack', 'Special Defense'
);
if ( defined $arg ) {
if ( defined $arg->{HP} ) {
$current_value &= ~0x1f;
$current_value |= ( $arg->{HP} & 0x1f );
}
if ( defined $arg->{Attack} ) {
$current_value &= ~( 0x1f << 5 );
$current_value |= ( ( $arg->{Attack} & 0x1f ) << 5 );
}
if ( defined $arg->{Defense} ) {
$current_value &= ~( 0x1f << 10 );
$current_value |= ( ( $arg->{Defense} & 0x1f ) << 10 );
}
if ( defined $arg->{Speed} ) {
$current_value &= ~( 0x1f << 15 );
$current_value |= ( ( $arg->{Speed} & 0x1f ) << 15 );
}
if ( defined $arg->{SpecialAttack} ) {
$current_value &= ~( 0x1f << 20 );
$current_value |= ( ( $arg->{SpecialAttack} & 0x1f ) << 20 );
}
if ( defined $arg->{SpecialDefense} ) {
$current_value &= ~( 0x1f << 25 );
$current_value |= ( ( $arg->{SpecialDefense} & 0x1f ) << 25 );
}
$self->_pokemon->{substructures}[3]{ivs_egg_status_and_ability} =
$current_value;
}
my $ivs_egg_status_and_ability = $current_value;
return {
HP => $ivs_egg_status_and_ability & 0x1F,
Attack => ( $ivs_egg_status_and_ability >> 5 ) & 0x1F,
Defense => ( $ivs_egg_status_and_ability >> 10 ) & 0x1F,
Speed => ( $ivs_egg_status_and_ability >> 15 ) & 0x1F,
SpecialDefense => ( $ivs_egg_status_and_ability >> 25 ) & 0x1F,
SpecialAttack => ( $ivs_egg_status_and_ability >> 20 ) & 0x1F,
};
}
sub evs {
my $self = shift;
my $arg = shift;
my $pokemon = $self->_pokemon;
my $substructure = $pokemon->{substructures}[2];
if (defined $arg) {
if (defined $arg->{HP}) {
$substructure->{hp_ev} = $arg->{HP};
}
if (defined $arg->{Attack}) {
$substructure->{attack_ev} = $arg->{Attack};
}
if (defined $arg->{Defense}) {
$substructure->{defense_ev} = $arg->{Defense};
}
if (defined $arg->{Speed}) {
$substructure->{speed_ev} = $arg->{Speed};
}
if (defined $arg->{SpecialAttack}) {
$substructure->{special_attack_ev} = $arg->{SpecialAttack};
}
if (defined $arg->{SpecialDefense}) {
$substructure->{special_defense_ev} = $arg->{SpecialDefense};
}
}
return {
HP => $pokemon->{substructures}[2]{hp_ev},
Attack => $pokemon->{substructures}[2]{attack_ev},
Defense => $pokemon->{substructures}[2]{defense_ev},
Speed => $pokemon->{substructures}[2]{speed_ev},
SpecialAttack => $pokemon->{substructures}[2]{special_attack_ev},
SpecialDefense => $pokemon->{substructures}[2]{special_defense_ev},
};
}
sub level {
my $self = shift;
my $arg = shift;
if ( defined $arg ) {
die 'Not implemented';
}
my $pokemon = $self->_pokemon;
my $experience = $pokemon->{substructures}[0]{experience};
my $pokemon = $self->_pokemon;
my $growth_func = sub {
my $n = shift;
if ($n == 1) {
if ( $n == 1 ) {
return 1;
}
return $self->growth_function->($n);
};
if ( defined $arg ) {
$pokemon->{substructures}[0]{experience} = $growth_func->($arg);
}
my $experience = $pokemon->{substructures}[0]{experience};
my $level = 1;
while ($level <= 100 && int($growth_func->($level)) <= $experience) {
while ( $level <= 100 && int( $growth_func->($level) ) <= $experience ) {
$level++;
}
$level -= 1;
@ -48,75 +132,76 @@ sub level {
}
sub growth_function {
my $self = shift;
my $self = shift;
my $growth = $self->growth;
if ($growth eq 'GROWTH_FAST') {
if ( $growth eq 'GROWTH_FAST' ) {
return \&_exp_fast;
}
if ($growth eq 'GROWTH_MEDIUM_FAST') {
if ( $growth eq 'GROWTH_MEDIUM_FAST' ) {
return \&_exp_medium_fast;
}
if ($growth eq 'GROWTH_MEDIUM_SLOW') {
if ( $growth eq 'GROWTH_MEDIUM_SLOW' ) {
return \&_exp_medium_slow;
}
if ($growth eq 'GROWTH_SLOW') {
if ( $growth eq 'GROWTH_SLOW' ) {
return \&_exp_slow;
}
if ($growth eq 'GROWTH_ERRATIC') {
if ( $growth eq 'GROWTH_ERRATIC' ) {
return \&_exp_erratic;
}
if ($growth eq 'GROWTH_FLUCTUATING') {
if ( $growth eq 'GROWTH_FLUCTUATING' ) {
return \&_exp_fluctuating;
}
}
sub gender_ratio {
my $self = shift;
my $self = shift;
my $pokemon_name = $self->pokemon_name;
my %pokemon_data = %Rsaves::Constants::Emerald::SpeciesData::SPECIES_DATA;
my $data = $pokemon_data{$pokemon_name};
my $data = $pokemon_data{$pokemon_name};
my $gender_ratio = $data->{gender_ratio};
return $gender_ratio;
}
sub gender {
# 0 male
# 1 female
# 2 genderless
my $self = shift;
my $pokemon = $self->_pokemon;
my $personality = shift // $pokemon->{personality};
my $self = shift;
my $pokemon = $self->_pokemon;
my $personality = shift // $pokemon->{personality};
my $gender_ratio = $self->gender_ratio;
if ($gender_ratio == 0) {
if ( $gender_ratio == 0 ) {
return 0;
}
if ($gender_ratio == 254) {
if ( $gender_ratio == 254 ) {
return 1;
}
if ($gender_ratio == 255) {
if ( $gender_ratio == 255 ) {
return 2;
}
if ($gender_ratio <= ($personality & 0xff)) {
if ( $gender_ratio <= ( $personality & 0xff ) ) {
return 0;
}
return 1;
}
sub personality {
my $self = shift;
my $arg = shift;
my $self = shift;
my $arg = shift;
my $pokemon = $self->_pokemon;
if (defined $arg) {
if ( defined $arg ) {
$self->_pokemon->{personality} = $arg;
}
return $pokemon->{personality};
}
sub growth {
my $self = shift;
my $self = shift;
my $pokemon_name = $self->pokemon_name;
my %pokemon_data = %Rsaves::Constants::Emerald::SpeciesData::SPECIES_DATA;
my $data = $pokemon_data{$pokemon_name};
my $data = $pokemon_data{$pokemon_name};
return $data->{growth_rate};
}
@ -144,9 +229,10 @@ sub _exp_medium_fast {
sub _exp_medium_slow {
my $n = shift;
#define EXP_MEDIUM_SLOW(n)((6 * CUBE(n)) / 5 - (15 * SQUARE(n)) + (100 * n) - 140) // (6 * (n)^3) / 5 - (15 * (n)^2) + (100 * n) - 140
my $return = (
( 6 * _cube($n) ) / 5 - ( 15 * _square($n) ) + ( 100 * $n ) - 140 );
my $return =
( ( 6 * _cube($n) ) / 5 - ( 15 * _square($n) ) + ( 100 * $n ) - 140 );
return $return;
}
@ -201,46 +287,53 @@ sub get_icon {
}
sub generate_personality {
my $self = shift;
my $target_shiny = shift;
my $target_gender = shift;
my $target_nature = shift;
my $otid = $self->otid;
my $self = shift;
my $target_shiny = shift;
my $target_gender = shift;
my $target_nature = shift;
my $otid = $self->otid;
my $should_search_gender = 0;
my $personality;
if (defined $target_gender && !(grep {$self->gender_ratio eq $_} (0, 254, 255))) {
if ( defined $target_gender
&& !( grep { $self->gender_ratio eq $_ } ( 0, 254, 255 ) ) )
{
$should_search_gender = 1;
}
if (defined $target_gender && $target_gender != 0 && $target_gender != 1) {
if ( defined $target_gender && $target_gender != 0 && $target_gender != 1 )
{
die "Incorrect gender $target_gender.";
}
if (defined $target_nature && ($target_nature < 0 || $target_nature > 24)) {
if ( defined $target_nature
&& ( $target_nature < 0 || $target_nature > 24 ) )
{
die "Incorrect nature $target_nature.";
}
for (my $i = 0; $i < 0xffffffff; $i++) {
if (defined $target_nature && $i % 25 != $target_nature) {
for ( my $i = 0 ; $i < 0xffffffff ; $i++ ) {
if ( defined $target_nature && $i % 25 != $target_nature ) {
next;
}
if (defined $target_shiny && !(!!$target_shiny == !!Rsaves::is_shiny($otid, $i))) {
if ( defined $target_shiny
&& !( !!$target_shiny == !!Rsaves::is_shiny( $otid, $i ) ) )
{
next;
}
if ($should_search_gender && $self->gender($i) != $target_gender) {
if ( $should_search_gender && $self->gender($i) != $target_gender ) {
next;
}
$personality = $i;
last;
}
if (!defined $personality) {
if ( !defined $personality ) {
warn "Could not find personality combination, this is a bug.";
}
return $personality;
}
sub otid {
my $self = shift;
my $arg = shift;
my $self = shift;
my $arg = shift;
my $pokemon = $self->_pokemon;
if (defined $arg) {
if ( defined $arg ) {
$pokemon->{otid} = $arg;
}
return $pokemon->{otid};

View File

@ -41,10 +41,16 @@ has _save_callbacks => ( is => 'rw', default => sub { [] } );
has _current_personality => ( is => 'rw' );
has _iv_buffers => ( is => 'rw', default => sub { {} } );
has _ev_buffers => ( is => 'rw', default => sub { {} } );
my $root = path(__FILE__)->parent->parent->parent->parent;
my $width = 600;
my $height = 400;
my @stats =
( 'HP', 'Attack', 'Defense', 'Speed', 'SpecialAttack', 'SpecialDefense' );
sub start {
my $self = shift;
my $window = Gtk4::Window->new;
@ -67,7 +73,7 @@ sub draw {
my $pokemon = $self->pokemon;
$window->set_title( 'Editing Pokémon '
. Rsaves::translate_3rd_encoding( $pokemon->nickname ) );
$self->_current_personality($pokemon->personality);
$self->_current_personality( $pokemon->personality );
my $grid = Gtk4::Grid->new;
$grid->set_column_homogeneous(1);
my $canvas = Gtk4::DrawingArea->new;
@ -94,52 +100,165 @@ sub draw {
my $string_list = Gtk4::StringList->new( [@species] );
my $save_button = Gtk4::Button->new_with_label('Save changes');
$self->_selected_species( $pokemon->species );
my $box_right_image = Gtk4::Box->new('vertical', 1);
my $box_right_image = Gtk4::Box->new( 'vertical', 1 );
$box_right_image->set_margin_top(30);
$box_right_image->set_valign('start');
$box_right_image->set_halign('start');
$grid->attach($box_right_image, 2, 0, 4, 2);
my $box_ev = Gtk4::Box->new('vertical', 1);
$grid->attach( $box_right_image, 2, 0, 2, 2 );
$grid->attach( $box_ev, 4, 0, 2, 2 );
$box_ev->append(Gtk4::Label->new('Select EV'));
$self->create_select_level($box_ev);
$self->create_select_evs($box_ev);
$self->create_change_nickname_entry($box_right_image);
$save_button->signal_connect(
clicked => sub {
$pokemon->species( $self->_selected_species );
$pokemon->personality($self->_current_personality);
for my $func (@{$self->_save_callbacks}) {
$pokemon->personality( $self->_current_personality );
$self->recalculate_ivs;
$self->recalculate_evs;
for my $func ( @{ $self->_save_callbacks } ) {
$func->();
}
$self->draw;
}
);
$self->draw_dropdown_pokemon_list($box_right_image);
$box_right_image->append(Gtk4::Label->new('Select IV'));
$self->create_select_ivs($box_right_image);
$self->create_modify_personality($grid);
$grid->attach( $save_button, 4, 7, 1, 1 );
$window->set_child($grid);
}
sub create_select_level {
my $self = shift;
my $box = shift;
my $pokemon = $self->pokemon;
my $box_level = Gtk4::Box->new('horizontal', 10);
$box_level->append(Gtk4::Label->new('Lvl:'));
my $entry = Gtk4::Entry->new;
$entry->set_input_purpose('digits');
$entry->get_buffer->set_text($pokemon->level, length $pokemon->level);
$box_level->append($entry);
$self->onSave( sub {
my $text = $entry->get_buffer->get_text;
if ($text !~ /^[0-9]+$/) {
return;
}
if ($text > 100) {
$text = 100;
}
if ($text < 2) {
$text = 2;
}
$pokemon->level($text);
});
$box->append($box_level);
}
sub recalculate_evs {
my $self = shift;
my $pokemon = $self->pokemon;
for my $key ( @stats ) {
my $text = $self->_ev_buffers->{$key}->get_text;
next if $text !~ /^[0-9]+$/;
if ($text > 255) {
$text = 255;
}
if ($text < 0) {
$text = 0;
}
$pokemon->evs(
{
$key => $text,
}
);
}
}
sub recalculate_ivs {
my $self = shift;
my $pokemon = $self->pokemon;
for my $key ( @stats ) {
my $text = $self->_iv_buffers->{$key}->get_text;
next if $text !~ /^[0-9]+$/;
if ($text > 31) {
$text = 31;
}
if ($text < 0) {
$text = 0;
}
$pokemon->ivs(
{
$key => $text,
}
);
}
}
sub create_select_evs {
my $self = shift;
my $box = shift;
my $ev_buffers = $self->_ev_buffers;
my $evs = $self->pokemon->evs;
for my $stat (@stats) {
my $box_stat = Gtk4::Box->new( 'horizontal', 10 );
my $entry = Gtk4::Entry->new;
$entry->set_input_purpose('digits');
$entry->get_buffer->set_text( $evs->{$stat}, length $evs->{$stat} );
$box_stat->append( Gtk4::Label->new($stat) );
$box_stat->append($entry);
$ev_buffers->{$stat} = $entry->get_buffer;
$box->append($box_stat);
}
}
sub create_select_ivs {
my $self = shift;
my $box = shift;
my $iv_buffers = $self->_iv_buffers;
my $ivs = $self->pokemon->ivs;
for my $stat (@stats) {
my $box_stat = Gtk4::Box->new( 'horizontal', 10 );
my $entry = Gtk4::Entry->new;
$entry->set_input_purpose('digits');
$entry->get_buffer->set_text( $ivs->{$stat}, length $ivs->{$stat} );
$box_stat->append( Gtk4::Label->new($stat) );
$box_stat->append($entry);
$iv_buffers->{$stat} = $entry->get_buffer;
$box->append($box_stat);
}
}
sub create_modify_personality {
my $self = shift;
my $grid = shift;
my $grid = shift;
my $button = Gtk4::Button->new_with_label('Edit personality');
$button->signal_connect(clicked => sub {
$self->open_window_personality;
});
$button->signal_connect(
clicked => sub {
$self->open_window_personality;
}
);
$button->set_valign('start');
$button->set_halign('start');
$grid->attach($button, 2, 2, 1, 1);
$grid->attach( $button, 2, 2, 1, 1 );
}
sub open_window_personality {
my $self = shift;
my $self = shift;
my $window = Gtk4::Window->new;
$window->set_default_size(600, 600);
my $scroll = Gtk4::ScrolledWindow->new;
my $box_window = Gtk4::Box->new('vertical', 1);
$box_window->append(Gtk4::Label->new('Select shiny'));
$window->set_default_size( 600, 600 );
my $scroll = Gtk4::ScrolledWindow->new;
my $box_window = Gtk4::Box->new( 'vertical', 1 );
$box_window->append( Gtk4::Label->new('Select shiny') );
my $box_shiny = Gtk4::Box->new('horizontal', 1);
my $box_shiny = Gtk4::Box->new( 'horizontal', 1 );
my $shiny_dont_mind = Gtk4::ToggleButton->new_with_label('Do not matter');
my $shiny_yes = Gtk4::ToggleButton->new_with_label('Yes');
my $shiny_no = Gtk4::ToggleButton->new_with_label('No');
my $shiny_yes = Gtk4::ToggleButton->new_with_label('Yes');
my $shiny_no = Gtk4::ToggleButton->new_with_label('No');
$shiny_dont_mind->set_active(1);
@ -151,12 +270,12 @@ sub open_window_personality {
$box_shiny->append($shiny_no);
$box_window->append($box_shiny);
$box_window->append(Gtk4::Label->new('Select gender'));
$box_window->append( Gtk4::Label->new('Select gender') );
my $box_gender = Gtk4::Box->new('horizontal', 1);
my $box_gender = Gtk4::Box->new( 'horizontal', 1 );
my $gender_dont_mind = Gtk4::ToggleButton->new_with_label('Do not matter');
my $gender_male = Gtk4::ToggleButton->new_with_label('Male');
my $gender_female = Gtk4::ToggleButton->new_with_label('Female');
my $gender_male = Gtk4::ToggleButton->new_with_label('Male');
my $gender_female = Gtk4::ToggleButton->new_with_label('Female');
$gender_female->set_group($gender_male);
$gender_dont_mind->set_group($gender_male);
@ -169,11 +288,12 @@ sub open_window_personality {
$box_window->append($box_gender);
$box_window->append(Gtk4::Label->new('Select nature'));
$box_window->append( Gtk4::Label->new('Select nature') );
my $box_natures = Gtk4::Box->new('vertical', 1);
my @natures = @Rsaves::Constants::Emerald::Natures::NATURES;
my %toggle_nature = map { $_ => Gtk4::ToggleButton->new_with_label($_) } @natures;
my $box_natures = Gtk4::Box->new( 'vertical', 1 );
my @natures = @Rsaves::Constants::Emerald::Natures::NATURES;
my %toggle_nature =
map { $_ => Gtk4::ToggleButton->new_with_label($_) } @natures;
my $nature_dont_mind = Gtk4::ToggleButton->new_with_label('Do not matter');
$nature_dont_mind->set_active(1);
@ -189,35 +309,39 @@ sub open_window_personality {
$box_window->append($box_natures);
my $button_generate = Gtk4::Button->new_with_label('Generate personality');
$button_generate->signal_connect(clicked => sub {
my @gender_buttons = ($gender_male, $gender_female);
my $target_shiny = undef;
my $target_gender = undef;
my $target_nature = undef;
if ($shiny_yes->get_active) {
$target_shiny = 1;
}
if ($shiny_no->get_active) {
$target_shiny = 0;
}
for my $i (0..1) {
if ($gender_buttons[$i]->get_active) {
$target_gender = $i;
last;
$button_generate->signal_connect(
clicked => sub {
my @gender_buttons = ( $gender_male, $gender_female );
my $target_shiny = undef;
my $target_gender = undef;
my $target_nature = undef;
if ( $shiny_yes->get_active ) {
$target_shiny = 1;
}
}
for my $i (0..24) {
if ($toggle_nature{$natures[$i]}->get_active) {
$target_nature = $i;
last;
if ( $shiny_no->get_active ) {
$target_shiny = 0;
}
for my $i ( 0 .. 1 ) {
if ( $gender_buttons[$i]->get_active ) {
$target_gender = $i;
last;
}
}
for my $i ( 0 .. 24 ) {
if ( $toggle_nature{ $natures[$i] }->get_active ) {
$target_nature = $i;
last;
}
}
my $personality =
$self->pokemon->generate_personality( $target_shiny,
$target_gender, $target_nature );
if ( defined $personality ) {
$self->_current_personality($personality);
}
$window->close;
}
my $personality = $self->pokemon->generate_personality($target_shiny, $target_gender, $target_nature);
if (defined $personality) {
$self->_current_personality($personality);
}
$window->close;
});
);
$box_window->append($button_generate);
$scroll->set_child($box_window);
$window->set_child($scroll);
@ -225,28 +349,29 @@ sub open_window_personality {
}
sub create_change_nickname_entry {
my $self = shift;
my $box = shift;
my $pokemon = $self->pokemon;
my $label = Gtk4::Label->new('Change the nickname:');
my $entry = Gtk4::Entry->new;
my $nickname = Rsaves::translate_3rd_encoding( $pokemon->nickname );
my $box_nickname = Gtk4::Box->new('horizontal', 10);
my $self = shift;
my $box = shift;
my $pokemon = $self->pokemon;
my $label = Gtk4::Label->new('Change the nickname:');
my $entry = Gtk4::Entry->new;
my $nickname = Rsaves::translate_3rd_encoding( $pokemon->nickname );
my $box_nickname = Gtk4::Box->new( 'horizontal', 10 );
$entry->get_buffer->set_text( $nickname, length $nickname );
$entry->set_halign('start');
$entry->set_valign('start');
$label->set_halign('start');
$label->set_valign('start');
$box_nickname->append( $label);
$box_nickname->append( $entry);
$box_nickname->append($label);
$box_nickname->append($entry);
$box->append($box_nickname);
$self->onSave(
sub {
my $translated_nickname = Rsaves::to_3rd_encoding($entry->get_buffer->get_text);
my $translated_nickname =
Rsaves::to_3rd_encoding( $entry->get_buffer->get_text );
if ( length $translated_nickname < 10 ) {
$translated_nickname .= chr(0xff);
my $length = length $translated_nickname;
$translated_nickname .= (chr(0x00)) x (10 - $length);
$translated_nickname .= ( chr(0x00) ) x ( 10 - $length );
}
if ( length $translated_nickname > 10 ) {
$translated_nickname = substr $translated_nickname, 0, 10;
@ -259,7 +384,7 @@ sub create_change_nickname_entry {
sub draw_dropdown_pokemon_list {
my $self = shift;
my $box = shift;
my $box = shift;
my $pokemon = $self->pokemon;
my $button_box = Gtk4::Box->new( 'horizontal', 1 );
my $box_popover = Gtk4::Box->new( 'vertical', 1 );
@ -309,9 +434,9 @@ sub draw_dropdown_pokemon_list {
$popover_dropdown->set_size_request( 100, 400 );
$button_box->append($popover_dropdown);
my $label_button = Gtk4::Label->new('Change species:');
my $box_dropdown = Gtk4::Box->new('horizontal', 10);
$box_dropdown->append( $label_button);
$box_dropdown->append( $button);
my $box_dropdown = Gtk4::Box->new( 'horizontal', 10 );
$box_dropdown->append($label_button);
$box_dropdown->append($button);
$box->append($box_dropdown);
$label_button->set_valign('start');
$label_button->set_halign('start');