Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid

Advice on Perl OO program structure (MUD)

by yoda54 (Monk)
on Jun 24, 2006 at 04:15 UTC ( #557353=perlquestion: print w/replies, xml ) Need Help??
yoda54 has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks, I seek your enlightenment so that I may one day fulfill my life's dream of writing my own MUD. The following contains some simple code please comment. Am I utilizing OO correctly? Would my example be correct for sending objects to combat? Thanks Monks

#!/usr/bin/perl use strict; use warnings; use Combat; use Peasant; my $henry = Peasant->new(name => "Henry"); my $joe = Peasant->new(name => "Joe"); my $combat = Combat->new(name => "combat object"); $combat->initiative($joe, $henry); $combat->combat($joe, $henry); ------------------------------------------------------------ package Combat; use strict; use warnings; sub new { my ($class, %arg) = @_; my $objref = { _turns => $arg{name} || 0, _name => $arg{name} || "unknown" }; bless $objref, $class; return $objref; } sub combat { my ($combatref, $self, $self2) = @_; $self->attack($self2); $self2->attack($self); } sub initiative { my ($combatref) = shift @_; my @combatants = @_; foreach(@combatants) { #check initiative code here } } 1; ------------------------------------------------------------ package Peasant; use strict; use warnings; sub new { my ($class, %arg) = @_; my $objref = { _name => $arg{name} || "unknown", _dex => $arg{dex} || int(rand(10)) + 2, _hp => $arg{hp} || int(rand(10)) + 2, _gold => $arg{gold} || int(rand(30)) + 2, _title => $arg{title} || "farmboy" }; bless $objref, $class; return $objref; } sub attack { my ($self,$target) = @_; my $amount = int(rand(10)) + 2; print "$self->{_name} attacks $target->{_name} for $amount!\n" +; $target->damage( $amount, $self ); } sub acquiregold { my ($self, $amount) = @_; $self->{_gold} += $amount; } sub damage { my ($self, $amount, $attacker) = @_; $self->{_hp} -= $amount; if ( $self->{_hp} <= 0 ) { print "$attacker->{_name} has killed $self->{_name}!!! +\n"; print "$attacker->{_name} gained $self->{_gold} gold!! +!\n"; $attacker->acquiregold($self->{_gold}); } } sub hitpoints { my ($self, $hits) = @_; print "$self->{_name} HP $self->{_hp}\n"; } 1;

Considered by CountZero: Delete: no content
Content restored by Arunbear, using the mirror of g0n.

Replies are listed 'Best First'.
Re: Advice on Perl OO program structure (MUD)
by Joost (Canon) on Jun 24, 2006 at 10:37 UTC
    It looks like a fairly good setup. I do have a few suggestions.

    • I'm not sure the Combat object is needed right now; it's possible to convert Combat's methods to methods of "Peasant" (or better yet, a base class for Peasant). It depends a bit on how you mean to extend it.

    • In the combat() method you have ($combatref, $self, $self2) = @_;. It's a good idea to have $self always be the current object, not another argument. I would probably write it as ($self, $combatant1, $combatant2) = @_;.

    • I would move the item transfer code to the combat() method, and let the damage() method return the health of the combatant. This also implies you need inventory and/or gold accessors.

    • You shouldn't access other object's internal state directly; make methods to access them. I.e. don't use $attacker->{_name}, use $attacker->name().

      This will allow you to change the implementation (as a silly example, you might want a creature that looks different each time you look at it - that's easy to implement if you get the name from a method, but it gets really messy if you try to pull the name out of the objects hash).

    • You'll want to create a base class for characters/monsters. That will allow you to create new "creature classes" quickly. Just split out the reusable methods in a seperate class when you start adding creatures.

    update: I just noticed a fairly significant assumption in your code, namely that a Peasant is a (mostly) permanent classification. In other words, by making Peasant a class, you make it hard to "upgrade" a character from say a peasant to a merchant, warrior or whatever (assuming Merchant and Warrior are separate classes). That might not be a wise choice if the Peasant class is a player-character (NPCs don't usually change class, but players do sometimes upgrade in MUDs I know)

    There are ways to work around that in perl - at the most basic level, you can just re-bless() an object into another class - but if you're working with additional libraries (say - Class::DBI to store your data) that could become cumbersome. Most libraries consider classes to be static, even though that's not a requirement in Perl.

    If Peasant objects regularly change "class" you might want to consider storing the "class" in a field instead. I.e. make a PlayerCharacter class that has a "character_type" field or something similar. If your "classes" are really very different in behaviour, your original setup might be more useful, though. As in all software design, it depends. :-)

Re: Advice on Perl OO program structure (MUD)
by hv (Parson) on Jun 24, 2006 at 11:07 UTC

    I agree with Joost that this is a perfectly reasonable start.

    You'll want to consider various things for getting further from here:

    • class structure: you almost certainly want a base class for monsters/people. One important question is whether the characters controlled by players are "just another monster" or a separate class - I favour the first approach, since it means that (for example) it is easy to let players be any sort of creature, and automatically adopt the limitations and capabilities of that creature. However the second approach gives you flexibilities in other ways - you don't have to consider how arbitrary monsters level up, and it's much easier to retain game balance with player-specific tweaks.
    • class structure: you'll probably want a base class for locations. If you want consistent physics and large-area effects such as weather, you'll need to know the size of each location and take some care to ensure that things fit together consistently; if not, you can get away with not much more than a description and a list of exits.
    • class structure: you'll probably want a base class for items, that gives underlying support for things like weight/size and value.
    • namespace: as the number of classes starts to expand, the danger increases that you'll use a classname that clashes with one already in use on CPAN or in the perl core. I'd suggest from the start picking a name for the MUD and putting all your classes under that namespace, like "YourMudName::Peasant".
    • communication: you'll soon want to consider what is required to support multiple users (without which you only have an UD :) - each time something occurs that should result in a message, you need somehow to know who is in range to be informed about it: you need in some sense a list of observers of each location. If you can find a nice generalised way to handle that, it would probably be a great help in debugging - you could have (for example) a global monitor that observed everything, that wrote all such messages to a log file.

    Hope this helps some,


[OT] Re: Advice on Perl OO program structure (MUD)
by jhourcle (Prior) on Jun 24, 2006 at 13:52 UTC

    Unfortunately, I don't have any of the code left from when I was re-writing the combat system for a mud that never got off the ground.

    I'd suggest trying to get as much experience writing muds in existing mudlibs first, so you can decide what features you'd like, and which one's aren't as important for what you're trying to prove.

    I doubt I'd ever write a mud from scratch -- there are too many generic things that every mud has, that isn't of interest tome to program -- I'd want to focus on the sections that I could improve. Look at PerlMUD, or some of the LP mudlibs,

    That being said -- here's how we structured the object inheritance on the most recent mud I coded on: (working from many years old memory ... it was probably not this linear)

    ability / property objects inherited by -> generic creature / generic player objects inherited by -> generic race objects inherited by -> creature library objects inherited by -> specific creature objects

    So, in your case, we'd have 'attackable' as a property, which includes HP as a property, and ways to damage the object. 'can attack' is an ability, which would have ways for the object to deal damange. Most coders wouldn't reference these directly, but would inherit from the generic creature object, which contained those and other properties necessary.

    (come to think of it ... I think we had a 'set_race' function in the generic creature, which would dynamically inherit from the appropriate race object -- rases set up intrensic abilities and body structure (eg, which / how many of each armour type it could wear, how many weapons it could wield, base attack speed / strength, damage type resistance / weaknesses (eg, a fire elemental is hurt more by cold, but healed by fire attacks)))

    There were then a generic set of creatures that people could inherit, so that we'd make sure that a creatures were consistent between areas coded by different people. (similar hp, xp, damage, item drops, etc.)

    And, for those special creatures, area designers could inherit a library monster / object, and give it some tweaks


    There's way too much info about coding muds than I can easily post in this forum, and there are plenty of other people out there with more skills than I have. I'd suggest that you try lurking in a mud-specific forum. (it may be that you have to specifically wiz on a mud to get to some forums, but there used to be some good usenet newsgroups for this sort of discussion)


Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://557353]
Approved by GrandFather
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (3)
As of 2018-04-27 01:48 GMT
Find Nodes?
    Voting Booth?