Rolless in Seattle

I left off from my last D&D post still looking for a role, but at least I settle on simple base class 'Ability' which I will extend into six abilities.

Now looking at each ability they all have differing effects depending on the value. So lets look at 'Intelligence' this time. According to the 'rules' it seems the more Intelligence you have the more languages your character can speak,as well for Magic User characters it betters the chances of your character learning a 'spell', sets higher the maximum and minimum number of spells they can learn per level and sets the highest level spell they can use. It even limits you on what race or class your character can take on. However, having high intelligence does not impart spell ability it only enhances it if you happen to be a MU. Likewise very low inelegance does not stop you from speaking at least one language.

So Intelligence is a set of modifiers

  • Additional Language Ability

  • Magic Ability

  • Character Class

  • Character Race

So knowing the above at some point I will want something like this for all charactes.


sub learn_language{
   my $self = shift;
   my ($language) = @_;
   return 
      if (scalar($self->languages) >= $self->intelligence->max_languages());
   push(@{$self->languages()},$language);
}

and it will work fine 'max_languages' is just a little sub in my 'Intelligence' class say like this



sub max_languages {
   my $self = shift;
   my %additional = (3=>0,.....18=>7);
   return 9
      if ($self->current() >=18);
   return 1+$additional{$self->current()};

}

Ok that is easy enough but perhaps I am selling roles a little short here. If 'max_languages' was in my 'Character' class I would not have to bother with a call like this;

$self->intelligence->max_languages()

I would just have to call my 'max_languages' in my character class like this


$self->max_languages();

Ok but how do I get there?

Roles are easy enough with Moose all I have to do is change a few lines, I will start with my 'Ability' class and convert it to a role. Despite perl's TIMTONTDO philosophy, real life does need some balance and direction so with Moose you cannot use 'extend' with roles but you can use a roles with roles. So here we go



package Ability;
use Moose::Role;

has 'init' => (
      is  => 'rw',
      isa => 'Int',
  );

has 'current' => (
      is  => 'rw',
      isa => 'Int',
  );

That was easy all I really did was add in '::Role' at the end of 'use Moose'. You will also notice I have removed the 'Name' attribute. I still need it, of course, but there is one problem with roles you cannot use '+' to modify attributes in a sub class so I had to move it into the 'Intelligence' role like this. This does add in a few extra lines of code to my classes but at this point I will just turn a blind eye to that. So now I have


package Intelligence;
 
use Moose::Role;
with "Ability";

has 'name' => (
      is  => 'ro',
      isa => 'Str',
      init_arg => undef,
      default=>'Intelligence');
      
sub max_languages {
	my $self = shift;
	return 1
		if ($self->current()<8);
	return 8
		if ($self->current() >=18);
	my %additional = (8=>1,9=>1,10=>2,11=>2,12=>3,13=>3,14=>4,15=>4,16=>5,17=>6);
	return 1+$additional{$self->current()};
}

This time instead of using 'extends' I used 'with', so I am saying use my class 'with' this role, quite simple. So now in my 'Character' Class I have;


package Character;

use Moose;
with qw( Intelligence );  

has 'name' =>(
	is		=>'ro',
	isa		=>'Str',
);

Lets look a character with '12' inelegance

use Character;
my $str = Character->new({name=>'sir_cumference',current=>12});

print $str->name()."\n";
print $str->max_languages()."\n";

and I get this'


sir_cumference
4
Sweet and Moose is even smart enough to take 'current' in on the new hash and set the value for me. Things couldn't be better.

Opps!! Hold on I have six Abilities not one! So now how do I know 'current' is current Intelligence or current Strength?

I could do the silly thing and rename everything to 'Current_Intelligence' or silliness like 'c_Strength', I could go back to old school and just extend 'Character' but I still have to rename things! So it looks like I am taking the slow boat back to square one with no reuse of code and even venturing into anti-pattern land!!!.


Rottenecards_4783526_5ttqpmz7k4.png

I guess I will have to sleep on it again.


1 Comment

There's one thing i'd like to point out: Made-up examples of classes usually fall flat because they're nothing like what would be implemented in real world code. You're not quite at the "cat, dog, tree" level, but your examples here are fairly close.

Instead i'd recommend you break out the trusty grep.cpan.me and inspect real code using the things you wish to learn about and try to understand why they did what they did.

Leave a comment

About byterock

user-pic Long time Perl guy, a few CPAN mods allot of work on DBD::Oracle and a few YAPC presentations