package LasTres::Battle; use v5.36.0; use strict; use warnings; use feature 'signatures'; use Moo; use UUID::URandom qw/create_uuid_string/; use LasTres::Util; use LasTres::EnemyTeam; use JSON qw//; has uuid => ( is => 'rw', default => \&_build_uuid, ); has team1 => ( is => 'rw', required => 1, ); has team2 => ( is => 'rw', ); has last_frame => ( is => 'rw', required => 1, ); sub sanity_check ($self) { my $team1 = $self->team1; my $team2 = $self->team2; if ( !defined $team2 ) { return 0; } if ( !defined $team1->current_battle || !defined $team2->current_battle || $team1->current_battle ne $self->uuid || $team2->current_battle ne $self->uuid ) { return 0; } return 1; } sub winner ($self) { if ( $self->team2->is_defeated ) { return $self->team1; } if ( $self->team1->is_defeated ) { return $self->team2; } return; } sub try_turn ( $self, $entity ) { my $combat_target = $entity->combat_target; my $enemy_team = $self->get_enemy_team( $entity->team ); my @combat_members_enemy = @{ $enemy_team->combat_members }; @combat_members_enemy = grep { $_->health > 0 } @combat_members_enemy; if ( $entity->health <= 0 ) { # This entity is out of the battle. return; } if ( defined $entity->combat_target ) { { my @target_list_only_one_should_match = grep { $combat_target eq $_->uuid } @combat_members_enemy; if ( !scalar @target_list_only_one_should_match ) { undef($combat_target); next; } $combat_target = $target_list_only_one_should_match[0]; } } if ( !defined $combat_target ) { $combat_target = $combat_members_enemy[ LasTres::Util::rand_range_int( 0, $#combat_members_enemy ) ]; $entity->combat_target( $combat_target->uuid ); $entity->update; } $entity->attack($combat_target); } sub get_enemy_team ( $self, $team ) { if ( $self->team1->uuid ne $team->uuid ) { return $self->team1; } return $self->team2; } sub loser ($self) { if ( $self->team2->is_defeated ) { return $self->team2; } if ( $self->team1->is_defeated ) { return $self->team1; } return; } sub end ($self) { require LasTres::Redis; my $redis = LasTres::Redis->new; $self->team1->current_battle(undef); $self->team1->update; if ( defined $self->team2 ) { $self->team2->current_battle(undef); $self->team2->update; } $redis->db->expire( $redis->battle_key( $self->uuid ), 0 ); $self->team1->on_end_combat; $self->team2->on_end_combat; } sub _build_uuid { return create_uuid_string(); } sub to_json ($self) { return { uuid => $self->uuid, team1 => $self->team1->to_serializable, team2 => $self->team2->to_serializable, last_frame => $self->last_frame, }; } sub save_redis ($self) { require LasTres::Redis; my $redis = LasTres::Redis->new; $redis->db->setex( $redis->battle_key( $self->uuid ), 3600 * 6, JSON::to_json( $self->to_json ) ); } sub get_redis ( $class, $uuid ) { require LasTres::Redis; my $redis = LasTres::Redis->new; my $hash_self; eval { $hash_self = JSON::from_json( $redis->db->get( $redis->battle_key($uuid) ) ); }; if ($@) { return; } return $class->from_json( $uuid, $hash_self ); } sub from_json ( $class, $uuid, $hash ) { # I do not take the hash uuid since if redis lies that could be really bad. return $class->new( uuid => $uuid, team1 => $class->deserialize_team( $hash->{team1} ), team2 => $class->deserialize_team( $hash->{team2} ), last_frame => $hash->{last_frame}, ); } sub deserialize_team ( $class, $team_hash ) { if ( $team_hash->{is_db} ) { return LasTres::Schema::Result::Team->from_serializable($team_hash); } return LasTres::EnemyTeam->from_serializable($team_hash); } sub start_battle_machine ( $class, $team, $enemy_area_array ) { my @enemy_area_array = @$enemy_area_array; my $battle = LasTres::Battle->new( team1 => $team, last_frame => 0 ); my $team2 = LasTres::EnemyTeam->new( current_battle => $battle->uuid ); my @enemies_array = ( map { $_->generate($team2) } @$enemy_area_array ); my @enemy_log_list; my $first_log = 1; $team2->members( \@enemies_array ); $battle->team2($team2); $battle->save_redis; $team->current_battle( $battle->uuid ); for my $member ( $team2->combat_members->@* ) { if ( !$first_log ) { push @enemy_log_list, { text => ', ' }; } $first_log = 0; push @enemy_log_list, { color => 'red', text => $member->nick, }; push @enemy_log_list, { text => " Nivel @{[$member->level]} Salud @{[$member->health]}/@{[$member->max_health]}" }; } for my $member ( $team->members ) { $member->append_log_line( [ { text => 'Empieza un combate contra: ' }, @enemy_log_list ] ); } $team->update; return $battle; } 1;