[DSB Tutorial 4] Custom Methods and Monsters
Posted: Tue Apr 26, 2011 12:22 am
Lua Tables: A Crash Course!
Before we begin, let's talk briefly about Lua tables. Lua tables are defined within { }'s, with a comma-delimited list of elements. { } alone is an empty table, which isn't useful by itself, but is necessary to create a table if you want to add elements to it. Each table can have an arbitrary number of elements, which are accessed via the syntax table[element]. If the name of an element is a string, a short form with a dot can be used. That is, table["thing"] and table.thing are identical.
The following code creates a new table, t, and sets the element x equal to 1.
You can then modify the value of x by setting t.x.
Please note the following important distinction.
Attack Methods!
Creating a custom Thing is the same as creating a custom Wallitem or any other type of object. Just add an entry to the obj table, either by defining the new archetype explicitly or by using clone_arch to base it on an existing one.
Here is a clone of a normal sword. It simply adds "(MAGIC)" to its description if examined, and will inflict a bit of extra damage in battle.
That's not so special, though. Let's add the "DISRUPT" method to our magic sword, so that it can harm nonmaterials just like a vorpal blade. That will make it truly magical.
Here are the methods for a normal obj.sword:
As you can see, the code defines four things: the name of the method, the required level, the required skill (and, optionally, a subskill), and the function invoked to carry out the method. We'll get rid of "PARRY" and replace it with "DISRUPT". Naturally, we'll want to change the skill required to wizard, too.
Here's a new magic sword that does that:
Creating A New Method!
As long as you only use attack methods that have already been defined in DM, that's all you need to do. What if you want to create an entirely new attack method, though? You'll have to fill in some method_info in order to tell the DSB base code about your new attack method, such as how much experience it gives, whether it uses a charge from the item, and so on. You can either modify the global method_info table, or create a method_info table inside of your new weapon's properties to define methods that are local to that weapon.
For example, let's create a "door opener" object that shoots zo spells.
While we're here, take a look at that convert_deplete property. That tells DSB what the object should turn into when all of its charges are depleted. We'll need to define that object, too.
In this form, it will sort of work, but DSB will spit out errors because the "ZO" method is unknown. Let's remedy that by adding a method_info entry to our door_opener.
Creating A Monster!
Now that we can create new attack methods, let's create some new creatures to beat up on with the attack methods.
As usual, clone_arch is our best friend. That allows us to create a monster very similar to the one we want to modify, only changing a few basic properties. Useful things to mess around with include act_rate, how fast the monster can act; hp, the monster's base hit points; quickness which determines how easily it hits party members and base_power which determines how hard it hits when it does; and attack_type, which controls the sort of melee attack the monster uses. Monsters that shoot things have missile_type and missile_power.
For example, here's a swamp slime that shoots fireballs instead of poison. Extra dangerous!
While the monster AI is somewhat arcane and difficult to modify, there are a few simple boolean variables that allow you to alter monster AI in interesting ways. These booleans are:
hover: The monster will float over pits and triggers.
stupid: The monster is exceptionally dim-witted. It won't use any clever tactics, and will frequently blunder into danger.
crafty: The monster will try to flank the party members and attack from interesting directions.
smart: The monster will coordinate attacks with other monsters.
swarmy: The monster will move around quickly, rapidly forming and disbanding groups and attempting to surround the party.
paranoid: The monster will do everything it can to avoid being in a place where it can be attacked.
pounces: The monster will pause briefly and then spring forth, moving and then attacking almost immediately.
counterattack: The monster will immediately respond with its own melee attack when it is attacked in melee, unless it has just attacked.
You can use these properties simply by setting them to true.
You can also learn about other useful monster properties by reading a monster's object definition in base/objects.lua. Most of the properties are reasonably self-explanatory if you have a grasp of the fundamentals of how DSB works.
Next time, we'll delve into even more complicated mechanics and Lua code.
Before we begin, let's talk briefly about Lua tables. Lua tables are defined within { }'s, with a comma-delimited list of elements. { } alone is an empty table, which isn't useful by itself, but is necessary to create a table if you want to add elements to it. Each table can have an arbitrary number of elements, which are accessed via the syntax table[element]. If the name of an element is a string, a short form with a dot can be used. That is, table["thing"] and table.thing are identical.
The following code creates a new table, t, and sets the element x equal to 1.
Code: Select all
t = { x = 1 }
Please note the following important distinction.
Code: Select all
-- The following line of code adds an element y to the table t.
t.y = 2
-- The following line of code creates a new table, with a single element y = 2.
-- Any previous table stored in t is gone, as this overwrites t with a COMPLETELY NEW table, containing ONLY the element y.
t = { y = 2 }
Creating a custom Thing is the same as creating a custom Wallitem or any other type of object. Just add an entry to the obj table, either by defining the new archetype explicitly or by using clone_arch to base it on an existing one.
Here is a clone of a normal sword. It simply adds "(MAGIC)" to its description if examined, and will inflict a bit of extra damage in battle.
Code: Select all
obj.sword_magic = clone_arch(obj.sword, {
shortdesc = "MAGIC",
base_tpower = 40
} )
Here are the methods for a normal obj.sword:
Code: Select all
-- You don't need to copy this code. It is already defined in the base code.
obj.sword.methods = {
{ "SWING", 0, CLASS_FIGHTER, method_physattack },
{ "PARRY", 1, { CLASS_FIGHTER, SKILL_DEFENSE }, method_physattack },
{ "CHOP", 2, { CLASS_FIGHTER, SKILL_BASHING }, method_physattack }
}
Here's a new magic sword that does that:
Code: Select all
obj.sword_magic = clone_arch(obj.sword, {
shortdesc = "MAGIC",
base_tpower = 40,
methods = {
{ "SWING", 0, CLASS_FIGHTER, method_physattack },
{ "CHOP", 2, { CLASS_FIGHTER, SKILL_BASHING }, method_physattack },
{ "DISRUPT", 3, { CLASS_WIZARD, SKILL_DES }, method_physattack }
}
} )
As long as you only use attack methods that have already been defined in DM, that's all you need to do. What if you want to create an entirely new attack method, though? You'll have to fill in some method_info in order to tell the DSB base code about your new attack method, such as how much experience it gives, whether it uses a charge from the item, and so on. You can either modify the global method_info table, or create a method_info table inside of your new weapon's properties to define methods that are local to that weapon.
For example, let's create a "door opener" object that shoots zo spells.
Code: Select all
obj.door_opener = {
name = "DOOR OPENER",
type="THING",
class="MAGIC",
mass = 8,
icon=gfx.icons[65],
dungeon=gfx.dragon_spit,
methods = {
{ "SWING", 0, CLASS_FIGHTER, method_physattack },
{ "ZO", 2, { CLASS_WIZARD, SKILL_AIR }, method_shoot_spell }
},
def_charge=10,
convert_deplete="door_opener_x",
spell_power=150,
fit_chest = true,
fit_sheath = true
}
Code: Select all
obj.door_opener_x = clone_arch(obj.door_opener, {
methods = {
{ "SWING", 0, CLASS_FIGHTER, method_physattack },
}
} )
Code: Select all
obj.door_opener.method_info = {
ZO = {
xp_class = CLASS_WIZARD,
xp_sub = SKILL_AIR,
xp_get = 30,
idleness = 35,
stamina_used = 4,
mana_used = 70,
missile_type = "zospell",
ddefense = -7,
charge = 1
}
}
Now that we can create new attack methods, let's create some new creatures to beat up on with the attack methods.
As usual, clone_arch is our best friend. That allows us to create a monster very similar to the one we want to modify, only changing a few basic properties. Useful things to mess around with include act_rate, how fast the monster can act; hp, the monster's base hit points; quickness which determines how easily it hits party members and base_power which determines how hard it hits when it does; and attack_type, which controls the sort of melee attack the monster uses. Monsters that shoot things have missile_type and missile_power.
For example, here's a swamp slime that shoots fireballs instead of poison. Extra dangerous!
Code: Select all
obj.fireslime = clone_arch(obj.swampslime, {
missile_type = "fireball"
) }
hover: The monster will float over pits and triggers.
stupid: The monster is exceptionally dim-witted. It won't use any clever tactics, and will frequently blunder into danger.
crafty: The monster will try to flank the party members and attack from interesting directions.
smart: The monster will coordinate attacks with other monsters.
swarmy: The monster will move around quickly, rapidly forming and disbanding groups and attempting to surround the party.
paranoid: The monster will do everything it can to avoid being in a place where it can be attacked.
pounces: The monster will pause briefly and then spring forth, moving and then attacking almost immediately.
counterattack: The monster will immediately respond with its own melee attack when it is attacked in melee, unless it has just attacked.
You can use these properties simply by setting them to true.
Code: Select all
obj.mummy.counterattack = true -- Makes mummies fiercer
Next time, we'll delve into even more complicated mechanics and Lua code.