Object-orientated programming with Quest

The Pixie
Object-orientated Programming With Quest

The coding language of Quest is designed to be relatively simple, but it does support object-orientated programming (OOP). One of the problems with using Quest in this way is that "object" has a different meaning in text adventures leading to some potential confusion, however, they are not too different.

In OOP, all objects belong to a class, and in the Quest the equivalent of a class is a type. To learn about types, go to the "How to..." page, and read the following:

Use Types
Use Types and Tabs
More on Tabs (implementing a magic system)
Simple Combat System

I am going to assume you have read and understood them from here on.

The last one presents the "monster" type. It looks like this:
  <type name="monster">
<defence type="int">0</defence>
<armour type="int">0</armour>
<hitpoints type="int">10</hitpoints>
<take type="boolean">false</take>
<attack type="script"><![CDATA[
attackroll = GetRandomInt (1, 20) - this.defence + player.attack
attackroll = attackroll + player.equipped.attackbonus
if (attackroll > 10) {
damage = player.equipped.damagebonus
for (i, 1, player.equipped.damagedicenumber) {
damage = damage + GetRandomInt (1, player.equipped.damagedicesides) - this.armour
}
this.hitpoints = this.hitpoints - damage
if (this.hitpointsyour > 0) {
msg ("You swing " + player.equipped.alias + " and hit, doing " + damage + " points of damage; " + this.hurt)
}
else {
msg ("You swing your " + player.equipped.alias + " and hit, doing " + damage + " points of damage; " + this.death)
Death (this)
}
}
else {
msg ("You swing your " + player.equipped.alias + " and miss.")
}
]]></attack>
</type>

In OOP terms this is the class definition. It defines some attributes, defence, armour, etc. and an instance method, attack. An instance method is a function that belongs to an instance of the class; if we create a monster, it will have that script attached to it (Quest dos not support class method, in case you are wondering).


Inheritance

Inheritance allows the coder to go from the general to the specific. Let us say there will be a lot of undead in my game. Undead are a type of monster, but have their own properties. I can create a new type that is inherited from "monster", and so has all those methods and scripts, but with its own unique features.
  <type name="undead">
<inherit name="monster" />
<soulchill type="script"><![CDATA[
msg ("You feel a chill to the very core of your soul.")
]]></soulchill>
</type>

A class can inherit from a class that inherits from another class, so we can do this:
  <type name="ghost">
<inherit name="undead" />
<noncorporeal type="boolean">true</noncorporeal>
</type>

Quest allows multiple inheritance, which means an object or class can inherit from multiple parents. This object is inheriting from both "ghost" and "editor_object".
    <object name="ghost_of_lady_grey">
<inherit name="editor_object" />
<inherit name="ghost" />
</object>

This can potentially lead to some confusion. Suppose "ghost" sets the hit points to be 15, and "editor_object" sets it to 0; which one wins? The answer is that the one that come later in the list will overwrite the earlier value, so in the above example, the value from "ghost" will win.

You can test inheritance using the DoesInherit function.
DoesInherit(ghost_of_lady_grey, "editor_object")
DoesInherit(ghost_of_lady_grey, "ghost")
DoesInherit(ghost_of_lady_grey, "undead")
DoesInherit(ghost_of_lady_grey, "monster")

All these will evaluate to true... until you publish your game, when all the editor types will be stripped out!


Encapsulation

Encapsulation is the idea that all the code for something is hidden away with that thing, so other codes cannot mess around with it. Specifically;

Restrict access to certain components of the object
Bundle together all the code for an object with that object

Sadly Quest does not allow us to do the former, which does mean you can screw around with some pretty fundamental functions. However, we can do the latter. All you have to do is have all the code that relates to monsters defined inside the monster class, either in scripts or in delegates. Quest does not encourage you to do that (how many times have you used a delegate?),but the facility is there.


Polymorphism

Polymorphism is the idea that you can tell an object to do something without worrying about what it will do or how it will do it, or even what the object actually is. Quest is pretty big on polymorphism, though you may not realise it, and it is not necessarily proper OOP polymorphism. Play any Quest game and type GET for some object, and Quest will handle that; it does not matter if you are trying to get a door or a ball or the room you are in.

Let us say our ghost cannot be hurt by the player, being insubstantial, we could do this:
  <type name="ghost">
<inherit name="undead" />
<noncorporeal type="boolean">true</noncorporeal>
<attack type="script"><![CDATA[
msg ("You swing your " + player.equipped.alias + " and it goes right through the ghost!")
]]></attack>
</type>

The "attack" method here will now override the attack method of monster. The player types ATTACK GHOST, and Quest will use the "attack" script from the "ghost" type. When she types ATTACK GOBLIN, though, the normal attack script applies.

Want to add a new type? Perhaps robots with force fields act differently. How they act is kept with them; no need to touch the original code,

Think for a moment about the alternative. If "attack" was instead handled by a function, we would have to check whether the target was a ghost, and if so do one thing, and if not, do another. That is not so bad for one exception, but what if you have half a dozen monsters that behave a bit differently. Soon your function is huge, and the code for your ghost is lost in with the code for other monsters.


Message passing

This is the key feature of OOP, the concept of passing a message to an object, and letting it handle it itself. It fits naturally with encapsulation and polymorphism, so it has been covered already, it is just connecting that to the terminology.

In the above examples, Quest is passing the message "attack" to the monster, then letting the monster sort out what to do with it.
do(monster, "attack")

The Pixie
Incidentally, Quest offers an alternative to "do". At first glance these achieve the same thing:
do(monster, "attack")
invoke(monster.attack)

However, if you use "do", you have access to "this" in your script. If you use "invoke" you do not (because invoke has only been sent the script itself, not the object it is attached to). For this reason, "do" is far better.

HegemonKhan
Thanks for the explanation of this with regards to quest and the snippet too about do vs invoke. Useful for people like me, who're just learning the basics of programming.

This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums