4

I'm making a small text browser RPG game using PHP, MySQL and Java Script (and jQuery). All the items are saved in the MySQL database.

Every now and then a character have to make a skill roll (Talk, Climb, Sneak, etc. ), but I have the following problem - some of the items give passive bonuses to some skills. Let's say we have Official Suit item, it gives +1 to Talk rolls and One time use only - automatic success on a Talk roll.

My problem is the following - how can I implement a function for every item, that gives the bonuses or other options when the appropriate roll is made. I want when the player click the Talk option, a function to be run that makes the Talk roll and searches character's inventory for items that give him bonuses to that roll. If it sees that the player have such an item, let's say it's the Official Suit, it runs the item's function which gives the +1 Talk bonus and if the roll fails, the function gives an option to use the special ability of the item for automatic success.

So what is the best way to do this? Keywords to each item or some other way? I can't think of a scalable solution.

doppelgreener
  • 7,184
  • 7
  • 42
  • 68
Ziik
  • 143
  • 3

3 Answers3

2

Here is one way to do it:

CalculateCharacterStatEvent

Define a CalculateCharacterStatEvent with properties sender (the Character object that raised the event), stat (the name of the stat that is to be calculated), bonusAbsolute and bonusPercentageBased and some setter functions to manipulate the bonus properties, like increaseBonusAbsolute().

Items and event listeners

Add the item's bonuses in the item's reactToCalculateCharacterStatEvent() method. For example:

public function reactToCalculateCharacterStatEvent($event) {
    // The event listener only reacts if the talk skill is calculated
    if($event->getStat() == "talk") {
        // Increase the item bonus depending on the Character's intelligence
        if($event->sender->intelligence < 6) {
            $event->increaseBonusAbsolute(1);
        } else {
            $event->increaseBonusAbsolute(2);
        }
    }
}

Event listener registration

When you load the items, register the item's event handler reactToCalculateCharacterStatEvent at the Character object, so that it is called whenever the Character object raises a CalculateCharacterStatEvent.

Character class and event raisers

In the Character class, define functions that return character stats, like getAttribute() or getSkillLevel(). At the beginning of your getStat() functions, raise a CalculateCharacterStatEvent. This will give the item's event listeners a chance to respond to the event by changing its bonusAbsolute and bonusPercentage values. Use these values to modify the result of your getStat() methods. For example:

getSkillTalking() {
    $event = new CalculateCharacterStatEvent($this, "talking");
    $this->raiseEvent($event);
    $base = 5; // or some logic to determine the base stats
    return $base * $event->getBonusPercentage + $event->getBonusAbsolute();
}

Option to automatically succeed

In order to add functionalities to items, you can just add new event classes, event raisers in the Character class, and new event listeners in the item classes.

For example: you could add an event called FailedSkillCheckEvent and an event listener reactToFailedSkillCheckEvent() in the item classes. When a skill check fails, the Character class raises a FailedSkillCheckEvent. The items's event listeners then have an opportunity to react by adding recovery options to the event object. After raising the FailedSkillCheckEvent, the skill check method continues depending on whether or not there are recovery options listed in the FailedSkillcheckEvent.

Further enhancements

Decorators. This basic pattern can be made more flexible by turning the item's reactTo-methods into a decorator class (Decorator pattern), which can then also be used by other game elements like status effects, passive skills, etc.

Link with data. reactToCalculateCharacterStatEvent() and other event listeners can also be linked to the item's properties so that you can edit the bonuses in a database without having to change any code for bonus assignments. (You can still code unique item behavior if you want to.)

More complex events. In the examples above, the item's event listeners have access to the sender of the event, i.e. the Character object for whom some stat is to be calculated. You could define more complex events that can pass more information to the items's event listeners. For example, you pass an object that represents the enemy of the Character object in a battle context. Your items's bonuses could then depend on the enemy's stats:

// in Character class
getSkillMelee($battle) {
    $event = new CalculateCharacterBattleStatEvent($this, $battle->enemy, "melee");
    $this->raiseEvent($event);
    $base = 5; // or some logic to determine the base stats
    return $base * $event->getBonusPercentage + $event->getBonusAbsolute();
}

// in Item class
public function reactToCalculateCharacterBattleStatEvent($event) {
    if($event->getStat() == "melee") {
        // Increase the item bonus depending on the Character's enemy's intelligence
        if($event->enemy->intelligence < 6) {
            $event->increaseBonusAbsolute(1);
        } else {
            $event->increaseBonusAbsolute(2);
        }
    }
}
BerndBrot
  • 1,071
  • 6
  • 16
  • Thank you for the great answer! However, there are some things that I couldn't understand fully:

    1. What does the onCalculateCharacterStat function contain? Is it running the BonusCollectionEvent function or there is something that I'm missing?

    2. What is the best way to send, an enemy stat to the reactToOnCalculateCharacterStat function if I want the item to work for example only if the enemy have Intelligence below 6?

    – Ziik Aug 17 '12 at 17:28
  • Sorry for the confusion! onCalculateCharacterStat() is only needed in a particular framework in a particular language in which I am currently coding and that I had in mind when I was posting my answer. Please see my updated answer, which got rid of onCalculateCharacterStat() altogether. 2. See the example at the end.
  • – BerndBrot Aug 17 '12 at 22:53
  • What does the raiseEvent contain and how I register the item's event handler reactToCalculateCharacterStatEvent at the Character object? You answer is more than sufficient but I've never used events and event handlers in PHP until now and it's a little hard for me то understand it fully, sorry for that. – Ziik Aug 18 '12 at 09:52
  • "raiseEvent" is pseudo-code and "register event listener" is generic for "do whatever is needed in your programming language and/or framework to do this". :D Since you are committed to PHP, I recommend using a PHP framework (http://phpframeworks.com/). Yii, Symfony, and others have events (i.e. the Observer pattern) deeply integrated into their design, so that defining, dispatching, and reacting to events becomes very easy. See http://en.wikipedia.org/wiki/Observer_pattern for the Observer pattern and http://www.yiiframework.com/wiki/44/behaviors-events/ for how to do this in Yii. – BerndBrot Aug 18 '12 at 10:23
  • I'm using CodeIgniter and it looks like it doesn't have event dispatcher, so I will have to download a library for it (or maybe Symphony's can work with CodeIgniter's?). I knew that it would be better to use newer framework like Laravel maybe. Thank you very much for the time you spared explaining me all this stuff =] – Ziik Aug 18 '12 at 13:40
  • You're welcome. And don't be discouraged by Codeigniter not having event handling as part of their system: you can still implement the Observer pattern yourself. Be picky in case you decide to use a library. A quick google search yielded this one: https://github.com/dhorrigan/codeigniter-events. It adds event handling via static methods, which is considered bad programming practice (for good reason). Among other things, it makes testing your code more difficult. If in doubt, go to the framework's forum and ask the gurus what they consider the best extension. – BerndBrot Aug 18 '12 at 13:57
  • Okay, I'll see what I can do with this pattern. And one last question - why is it considered a bad programming practice? – Ziik Aug 18 '12 at 14:30
  • See http://www.youtube.com/watch?v=-FRm3VPhseI for some pointers. – BerndBrot Aug 18 '12 at 14:35