Jump to content

Recommended Posts

While I'm perfectly (and painfully) aware that PHP does not allow multiple inheritance of classes, I've run into a situation where that's what it looks like I need to do.  I can't quite figure out how to do this without making a million subvariants of every class combination I'd want.

 

Here's the problem (pseudo-code used here is just for example, not to be picked apart):

You are playing a game. Your character is both an Orc and a Paladin. Orcs have special stats and abilities, as do Paladins. Your enemies can also be Orcs, Paladins, or both, using the same stats and methods that you'd be getting.

 

I could easily "class OrcPaladin {}" or "class Player implements Orc, Paladin {}" but the former increases my class count exponentially for each race/class combination, and the latter only defines the CONTEXT of methods, but not specifics about them.  The methods for Orc and Paladin are unique to those classes, and the content of those methods are always the same.

 

ALL Paladins get the "public function prayForAbsolution(){}" function, and ALL of them implement it in the same way. ALL Orcs get the "public function berzerkerRage(){}" function, and ALL of them implement it in the same way.  These are the only things (for the sake of this example) that make up the Orc and Paladin abstract classes.

 

If I went with the interfaces method, I wouldn't be able to dynamically define the Player class within the bounds of Race and Class as defined by the USER without statically defining one or both.  I could predefine:

 

class Player {
  if ($this->class == 'Paladin') {
    $classAbility = 'prayForAbsolution';
  }
  public function prayForAbsolution() {}
  public function berzerkerRage() {}
  public function otherClassAbility() {}
  public function otherRaceAbility() {}
}

// and then elsewhere...
$you = new Player('Paladin');
$you->{$you->classAbility}();

 

...but that's an awful lot to load up into one class when only 2 methods will ever be needed.  Basically, I'd be throwing the whole slew of abilities into the Player class and lugging the whole catalog with it everywhere it went.  And that's not mentioning making enemies that can be Orcs or Paladins or Orc Paladins.

 

Am I missing something about interfaces that would negate this problem?  Am I wrong to want a Player object to be both an Orc type object and a Paladin type object?  What am I missing?

 

[EDIT] Oh, and hello :D

Link to comment
https://forums.phpfreaks.com/topic/191395-multiple-inheritance-i-know-i-know/
Share on other sites

You don't need to make a million different classes that'd be annoying :)

 

you could, however, instead of making the classes extensions of 1 big class you COULD simply make a few variables inside the player class

 

class Player {

  public function __construct($name,$race/* Orc.. etc */,$spec/* Paladin etc */) {

    eval("\$this->name = new $name;"); // = new Orc();

    eval("\$this->name = new $spec;"); // = new Paladin();

  }

}

 

then you can go thru each object to get the special abilities or whatever, however again, you could just use classkit its awesome :)

It sounds like you need a bridge or adapter (Bridge Pattern - Adapter Pattern).  You can have a Race class and a Class class.  Their concrete children can be members of a concrete PlayerCharacter class.  The biggest hurdle is attempting to access the abilities the various race/class combinations can potentially have.

 

What I did in my game was store class info in XML files so they could be completely modular.  I then made each ability/attack they could perform an instance of an Attack class.  A list of these attacks were then stored in my PC class.  Attacks were chosen based on the attack name - if found in the list, and if the PC had enough MP, the attack would be invoked.  Otherwise, the generic base attack (which all classes had) would be invoked.  My game didn't have various races for the player to choose from, but the same basic idea could work there, too.

 

Anyway, just some ideas.

class Player {

  public function __construct($name,$race/* Orc.. etc */,$spec/* Paladin etc */) {

    eval("\$this->name = new $name;"); // = new Orc();

    eval("\$this->name = new $spec;"); // = new Paladin();

  }

}

 

So that would be

$player->Orc->berzerkerRage();

and

$player->Paladin->prayForAbsolution();

to access each individual method. That wouldn't be bad, I guess, because the specific classes would still exist, AND I would only be instantiating the ones I needed.  Makes sense.

 

What I did in my game was store class info in XML files so they could be completely modular.  I then made each ability/attack they could perform an instance of an Attack class.  A list of these attacks were then stored in my PC class.  Attacks were chosen based on the attack name - if found in the list, and if the PC had enough MP, the attack would be invoked.  Otherwise, the generic base attack (which all classes had) would be invoked.  My game didn't have various races for the player to choose from, but the same basic idea could work there, too.

 

I had thought about this too - instantiating the attacks, which would then presumably be controlled by an interface to make sure it's got all of the appropriate methods (getViableTargets, getDamage, getDamageType, etc). That's probably a good idea in it's own right.

How about composite pattern?

 

class Character {
/*CharacterRace object*/
private $race;
/*CharacterClass object*/
private $class;

public function __construct(CharacterRace $race, CharacterClass $class) {
  $this->race = $race;
  $this->class = $class;
}

public function race() {
  return $this->race;
}

public function class() {
  return $this->race;
}

}

$OrcPaladin = new Character(new Orc(), new Paladin());

$OrcPaladin->race()->berzerkerRage();
$OrcPaladin->cass()->prayForAbsolution();

 

 

[/code]

Some pseudo-code:

 

class PlayerCharacter
{
   private $race;
   private $class;
   private $raceAbilities = array();
   private $classAbilities = array();

   public function __construct($race, $class)
   {
      $this->race = $race;
      $this->class = $class;

      $this->populate();
   }

   private function populate()
   {
      /* read race and class abilities from the db or XML file(s)
          for all abilities the PC is able to have access to 
          (e.g., those abilities labeled equal to or below their level)
          add to the arrays.

          while(iterating through datastore)
          {
              $raceAbilities[$row['name']] = new $row['type']($row['name'], $row['level'],  ... );
              //same for classAbilities
          } */
   }

   public function raceAbility($name)
   {
      // if $raceAbilities[$name] exists, execute it.  Else, execute default ability or do nothing
   }

   public function classAbility($name)
   {
      // if $classAbilities[$name] exists, execute it.  Else, execute default ability or do nothing
   }
}

abstract class RaceAbility
{
   protected $name;
   protected $level;
   // etc.

   public function __construct($name, $level, /* etc. */)
   {
      $this->name = $name;
      $this->level = $level;
      // etc.
   }

   abstract public function execute();
}

class OrcRage extends RaceAbility
{
   public function execute()
   {
      // do orc ragey things
   }
}

class Player {

  public function __construct($name,$race/* Orc.. etc */,$spec/* Paladin etc */) {

    eval("\$this->name = new $name;"); // = new Orc();

    eval("\$this->name = new $spec;"); // = new Paladin();

  }

}

 

So that would be

$player->Orc->berzerkerRage();

and

$player->Paladin->prayForAbsolution();

to access each individual method. That wouldn't be bad, I guess, because the specific classes would still exist, AND I would only be instantiating the ones I needed.  Makes sense.

 

What I did in my game was store class info in XML files so they could be completely modular.  I then made each ability/attack they could perform an instance of an Attack class.  A list of these attacks were then stored in my PC class.  Attacks were chosen based on the attack name - if found in the list, and if the PC had enough MP, the attack would be invoked.  Otherwise, the generic base attack (which all classes had) would be invoked.  My game didn't have various races for the player to choose from, but the same basic idea could work there, too.

 

I had thought about this too - instantiating the attacks, which would then presumably be controlled by an interface to make sure it's got all of the appropriate methods (getViableTargets, getDamage, getDamageType, etc). That's probably a good idea in it's own right.

 

it would be $thePlayer->race->ability();

 

and $thePlayer->spec->ability();

I'm not into (MMO)RPG so I'm quite clueless when it comes to modelling something like this. If you still need help - you mind explaining the mechanics? Like what is BerzekerRage and PrayForAbsolution exactly? Every child-class must be able to be subsituted with it's parent class because of this you need a general method that will invoke the appropriate abilities if the character meets the requirements (enough mana, ..) -> Specific business rules for your RPG Like Mchl mentioned you could use something like (altough I fail to see how the Composite pattern fits the bill):

 

class Character {
    private $race;
    private $class;
    
    public function __construct(CharacterRace $r, CharacterClass $c) {
        $this->race = $r;
        $this->class = $c;
    }
    
    public function getRace() { return $this->race; }
    public function getClass() { return $this->class; }
}

 

Then like Nightslyr said you can do:

 

$player->getRace()->executeSpecialAbility();
$player->getClass()->executeSpecialAbility();

 

Each race and class will now what it's special ability is and would execute it upon executeSpecialAbility() invocation.

 

But possibly you want something like:

 

$player1->attack($player2);

 

Then with "luck" and other things you could simulate an attack.

(altough I fail to see how the Composite pattern fits the bill):

 

Yeah... I mixed up names again.

 

 

Then like Nightslyr said you can do:

$player->getRace()->executeSpecialAbility();
$player->getClass()->executeSpecialAbility();

 

Each race and class will now what it's special ability is and would execute it upon executeSpecialAbility() invocation.

 

If each class/race has only one special ability, then this really looks like a fine solution to me.

 

If each class/race has only one special ability, then this really looks like a fine solution to me.

 

I have no knowledge of RPG's nor it's business rules which makes it quite hard to model a proper solution I read a background on WoW on Wikipedia altough it mentions more then the OP asked for like Factions and Faction specific races. It also probably has more then one special ability and these are probably influenced by other factors like "luck" and maybe some other stuff.

Actually, my solution was based on the name of the ability attempting to be invoked.  That way, each race and class could have a multitude of special abilities.  This solution works well in ASP.NET/C# - which is what I'm constructing my game with - as each button can have a CommandName attribute.  This attribute can be retrieved during an onclick event so the proper ability is invoked.  Something similar can happen in PHP, with each button's name attribute mapped to a specific attack.

 

My setup is essentially:

 

Iterate through the PC's list of attacks, creating a button for each one with a CommandName matching that particular attack.

|

|

V

Create the field of buttons with that info

|

|

V

When clicked, have the page post back to itself.  The results of the action depend on the name of the button that is clicked.  Results are echoed out to a div that contains the combat info.

 

It works pretty well.

You've all given me the answer to the problem, and I thank you for that.

 

Essentially, the goal was to maximize modularity in code so that, given enough effort by the player (and myself with front-end work) the end-result is a fairly complex game with many redeeming facets to keep the user's attention.

 

For example, in addition to being an Orc and a Paladin, you might want to also be a Blacksmith. Blacksmiths can do things that only blacksmiths can do, and so that's added into your character's repertoire, and provides another avenue for play. In addition to those, you might want to join the local 501st Stormtrooper Garrison. Or commence your training as a Librarian, which would get tacked on alongside Paladin.

 

In theory, every one of these additions could provide an entirely new layer to the game, whereby one feeds into the next; advancing in one that you may advance in others later. Orcs might have things only Orcs can do/make/get. Same with Paladins. Throw into the mix that every class falls into a category (Tank, Caster, Melee, etc) there's another level of "Hey you're X, come do Y" or "I notice you're X, if you beat the Z monster, I'll show you how to make Y." Maybe Librarians are the only ones that can access the Ninth Circle of Hell? You'd certainly want to put in all the work to become a Librarian to get all that phat underworld loot, especially if a rare Blacksmithing material you need is in abundance down there.

 

Add on top of that weapon-use and gaining experience with weapons as you use them. You might learn a sword-only ability by using your sword a bunch, which you could then upgrade by spending money at a trainer. For killing a bunch of undead, you might get invited to the Island of the Dead where you can kill more undead until you're blue in the face, and when THAT happens, you get an ability like Exorcism that kills the Undead on the spot.

 

Add in a Pokemon aspect where you collect things/pets, and level them up. Add in a collectible card catalog. Add in a gachapon collection. Take any part of any game, as long as it's definable in a non-animated way (unless AJAX is involved), and add it in. All you need is time and nimble fingers.

 

I've digressed from the original point, but you catch my drift. Dropping a bunch of classes in a directory, defining how you get from one class to another, and BAM - a game you can conceivably play forever (at least until boredom sets in).

 

I really like the idea presented earlier by Russell about having sub-objects... I think that best represents what I had in my head. Hopefully my idea as presented here doesn't sound too far-fetched :o

 

Thanks for the help!

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.