Last week, I was thinking about the enemies system because one of the things I have to implement these days is the boss of the second dungeon. Then, I don’t know what happened. My fingers must have slipped. I did not really realize it, but I wrote everything to manage enemies with scripts. And here we are: enemies are officially scripted now!

So far, each type of enemy was implemented as a C++ class (hardcoded into the engine). Creating a new type of enemy required to write a new C++ class that inherit the Enemy class. Even if the API was easy to use, implementing new enemy types was a quite heavy task. And the editor’s source code also had to be updated to support the new type of enemy. So this is was annoying. The other reason to make scripted enemies is that it will allow you to make your own enemies for your own quest. What you want when creating new types of enemy is to specify them as data files (a Lua script and some sprites), not to change the C++ engine.

So it’s done. I took 3 months to make scripted items, and 7 days to make scripted enemies. I’m surprised it is finished so soon. I was afraid it would be very hard, but thanks to the scripted items system, things were much easier that I thought. Most of the work was already done: for example, providing to Lua scripts some functions to manage sprites and movements. I started by writing the Lua script of each existing type of enemy. Then I deduced a list of 80 functions I needed to provide to Lua enemy scripts, and I implemented all of them. And that’s it! It just works!

And why it this so nice? It changes a lot. You want to write your own enemy (say, a spider)? Just create a file spider.lua that describes its properties and behavior:

-- spider.lua

function event_appear()
-- The enemy appears: set its properties
sol.enemy.set_life(1)
sol.enemy.create_sprite("enemies/spider")
sol.enemy.set_size(16, 16)
movement = sol.main.path_finding_movement_create(32)
sol.enemy.start_movement(movement)
end

The sol.module.something() functions are functions of the engine that a Lua script can call. sol.enemy represents the current enemy and provides some functions to set their properties and their dynamic behavior. The engine can also call some functions of your scripts (the ones prefixed by event_): the function event_appear() is called by the engine when a new instance of the enemy arrives on the map. Here, we set this spider’s initial properties: its life, its sprite (the corresponding sprite file must exist, and contain a small set of predetermined animations like "stopped", "walking", "hurt", etc.), its size and its movement. sol.main.path_finding_movement_create() creates a movement that calculates a path to the hero. That’s it, you have a new type of enemy. You can now add spiders to maps with the quest editor.

There are lots of functions like this. event_appear() is called by the engine to notify your enemy that it was just created on the map. Other useful callback functions are event_obstacle_reached(), event_movement_finished(), event_sprite_animation_finished(), event_dead(), etc.

After the release of ZSDX, I want to make some tutorials to show the elements of making a Solarus quest: maps, enemies, items, sprites, NPCs. In the meantime, you can have a look at the (updated) documentation.