The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package NetHack::Item::Role::Chargeable;
BEGIN {
  $NetHack::Item::Role::Chargeable::VERSION = '0.13';
}
use Moose::Role;

has recharges => (
    is        => 'rw',
    isa       => 'Int',
    predicate => 'recharges_known',
    clearer   => '_clear_recharges',
);
sub inc_recharges { $_[0]->recharges($_[0]->recharges + 1) }

has charges => (
    is        => 'rw',
    isa       => 'Int',
    predicate => 'charges_known',
    clearer   => 'set_charges_unknown',
);
sub subtract_charges { $_[0]->charges($_[0]->charges - $_[1]) }

has charges_spent_this_recharge => (
    traits    => ['Counter'],
    is        => 'rw',
    isa       => 'Int',
    default   => 0,
    handles   => {
        add_charges_spent_this_recharge   => 'inc',
        reset_charges_spent_this_recharge => 'reset',
    },
);

has times_recharged => (
    traits    => ['Counter'],
    is        => 'rw',
    isa       => 'Int',
    default   => 0,
    handles   => {
        inc_times_recharged => 'inc',
    },
);

sub spend_charge {
    my $self = shift;
    my $count = shift || 1;

    $self->add_charges_spent_this_recharge($count);

    return unless $self->charges_known;

    $self->subtract_charges($count);
    if ($self->charges < 0) {
        $self->charges(0);
    }

    return $self->charges;
}

sub recharge {
    my $self = shift;

    $self->set_charges_unknown;
    $self->inc_times_recharged;
    $self->reset_charges_spent_this_recharge;
    $self->inc_recharges if $self->recharges_known;
}

sub chance_to_recharge {
    my $self = shift;

    return undef if !$self->recharges_known;

    my $n = $self->recharges;

    # can always recharge at 0 recharges
    return 100 if $n == 0;

    # can recharge /oW only once
    return 0 if $self->has_identity
             && $self->identity eq 'wand of wishing';

    # can recharge all tools except magic marker indefinitely
    return 100 if $self->type eq 'tool'
               && $self->identity ne 'magic marker';

    # (n/7)^3
    return 100 - int(100 * (($n/7) ** 3));
}

after incorporate_stats => sub {
    my $self  = shift;
    # Counter sets the default to 0 for you, but we want it to be undef
    $self->_clear_recharges;
};

with 'NetHack::Item::Role::IncorporatesStats' => {
    attribute    => 'charges',
    defined_stat => 1,
};

with 'NetHack::Item::Role::IncorporatesStats' => {
    attribute    => 'recharges',
    defined_stat => 1,
};

around is_evolution_of => sub {
    my $orig = shift;
    my $new = shift;
    my $old = shift;

    if ($new->does(__PACKAGE__) && $old->does(__PACKAGE__)) {
        if ($old->recharges_known && $new->recharges_known) {
            # lost recharges?
            return 0 if $old->recharges > $new->recharges;

            # gained charges without a recharge?
            if ($old->charges_known && $new->charges_known) {
                return 0 if $old->recharges == $new->recharges
                        && $old->charges < $new->charges;
            }
        }
    }

    return $orig->($new, $old);
};

no Moose::Role;

1;