Hello, it's me again. I said in another topic that since I don't have enough stuff to present the project I'm working on, I'd share some scripts, so here I am.
From now on, maybe it's not very spectacular, but maybe it can help beginners with some skills. ^^
I was working on some enemies from a game named Chip's Challenge (a really good retro game by the way) and tried to implement them as enemies for the Solarus quest editor.
Fireballs and gliders
First, I did the fireball and the glider, two enemies which go straight until they hurt an obstacle. In that case, they go to another direction :
- the fireball goes to its right by default ; and if it's not possible, it goes to his left ; and if it's not possible (if there are walls on its left and on its right) it comes back.
- the glider goes to its left by default ; and if it's not possible, it goes to his right ; and if it's not possible (if there are walls on its left and on its right) it comes back.
If you don't see what I mean, I suggest you watch this video of a Chip's Challenge level (the 100th one), where there are only fireballs (the orange monsters) and gliders (the other ones) : http://www.youtube.com/watch?v=12E6DDiHpVE&hd=1 . You should see their movements.
I'll begin with the fireball (the glider is not very different).
First, create a sprite with 4 directions. For the example, I took the pike one, in order to have a more "Zeldastic" sprite instead of the Chip's Challenge one.
Put the sprite in the appropriate place, then create the .lua script associated.
First, we declare some local variables :
Then we do the enemy:on_created event. Do whatever you want, the only important thing is the line I put in bold : indeed, it will set a value for the sprite variable.
Let's do the enemy:on_restarted() event now. In this one, we'll tell our "pikeball" to go straight in its direction.
For this, we'll take the sprite direction and create a movement based on it :
Be careful with the line I put in bold, because it's a trap. You probably wonder why there is a "* 2". In fact, it's because of the numbering used in the Solarus engine.
You probably know there are eight possible directions, each one corresponding to a number :
In our case, the interesting directions/numbers are only horizontal and vertical ones (0, 2, 4 and 6).
The sprite we defined has four directions : nevertheless, they don't match with the ones we want. Indeed, the directions of the sprite are 0, 1, 2 and 3. So we have to multiply these directions by 2 to have the correct ones.
Then we can define the enemy:on_obstacle_reached() event, the most interesting one.
We can divide it into three parts :
For the first part, we'll need a complex condition such like this :
If true, it means that there is an obstacle on the monster's right ; so it will have to go left instead.
Let's define the direction for the monster's left :
As we don't want the monster to have a diagonal movement, we add 2 to the next_direction variable. We also have to "reset" it if it was equal to 6 : indeed, 6 + 2 = 8, but "8" isn't a good direction. So we restart it.
Now, let's define the "normal" monster's behavior :
It's like the previous piece of code, but in the other way.
Finally, let's make the monster restart in the new next_direction :
Note that you have to divide next_direction by 2 this time, for the same reason I was talking about some lines earlier.
Here's a tip. I previously said that the "pikeball" shall go back if it cannot go on this left nor on its right. But I didn't write anything about that case.
In fact, it's not necessary to write someting for it. Indeed, the first condition is quite efficient for that : it checks whether you can go on your right or not, and if not, the "pikeball" goes on its left. But if there's also something on the left, the enemy:on_obstacle_reached() event is activated again, and will test again if there is an obstacle on the right. And it will be the case since that obstacle is the one the "pikeball" hurt at first ; so its only choice will be left. And left + left = go back.
If you didn't understand what I said, don't worry, the most important thing to remember is that you don't have to bother with the "come back" case.
You can now insert your enemy in the quest editor and... enjoy !
Here is the complete .lua script for the fireball type enemy :
So much for the fireball type.
You can do the glider in order to practice. You'll just have to switch the directions in the enemy:on_obstacle_reached() event, it's as easy as pie.
From now on, maybe it's not very spectacular, but maybe it can help beginners with some skills. ^^
I was working on some enemies from a game named Chip's Challenge (a really good retro game by the way) and tried to implement them as enemies for the Solarus quest editor.
Fireballs and gliders
First, I did the fireball and the glider, two enemies which go straight until they hurt an obstacle. In that case, they go to another direction :
- the fireball goes to its right by default ; and if it's not possible, it goes to his left ; and if it's not possible (if there are walls on its left and on its right) it comes back.
- the glider goes to its left by default ; and if it's not possible, it goes to his right ; and if it's not possible (if there are walls on its left and on its right) it comes back.
If you don't see what I mean, I suggest you watch this video of a Chip's Challenge level (the 100th one), where there are only fireballs (the orange monsters) and gliders (the other ones) : http://www.youtube.com/watch?v=12E6DDiHpVE&hd=1 . You should see their movements.
I'll begin with the fireball (the glider is not very different).
First, create a sprite with 4 directions. For the example, I took the pike one, in order to have a more "Zeldastic" sprite instead of the Chip's Challenge one.
Code Select
walking enemies/pike.png 4 0 -1
0 0 16 16 8 13 1 1
0 0 16 16 8 13 1 1
0 0 16 16 8 13 1 1
0 0 16 16 8 13 1 1
Put the sprite in the appropriate place, then create the .lua script associated.
First, we declare some local variables :
Code Select
local enemy = ...
local sprite
local next_direction
Then we do the enemy:on_created event. Do whatever you want, the only important thing is the line I put in bold : indeed, it will set a value for the sprite variable.
Code Select
function enemy:on_created()
self:set_life(1)
self:set_damage(2)
[b]sprite = self:create_sprite("enemies/pike_clockwise")[/b]
self:set_size(16, 16)
self:set_origin(8, 13)
self:set_can_hurt_hero_running(true)
self:set_invincible()
self:set_attack_consequence("sword", "protected")
self:set_attack_consequence("thrown_item", "protected")
self:set_attack_consequence("arrow", "protected")
self:set_attack_consequence("hookshot", "protected")
self:set_attack_consequence("boomerang", "protected")
end
Let's do the enemy:on_restarted() event now. In this one, we'll tell our "pikeball" to go straight in its direction.
For this, we'll take the sprite direction and create a movement based on it :
Code Select
function enemy:on_restarted()
[b]next_direction = sprite:get_direction() * 2[/b]
local m = sol.movement.create("path")
m:set_path{next_direction}
m:set_speed(128)
m:set_loop(true)
m:start(self)
end
Be careful with the line I put in bold, because it's a trap. You probably wonder why there is a "* 2". In fact, it's because of the numbering used in the Solarus engine.
You probably know there are eight possible directions, each one corresponding to a number :
- 0 : right
- 1 : top-right
- 2 : top
- 3 : top-left
- 4 : left
- 5 : bottom-left
- 6 : bottom
- 7 : bottom-right
In our case, the interesting directions/numbers are only horizontal and vertical ones (0, 2, 4 and 6).
The sprite we defined has four directions : nevertheless, they don't match with the ones we want. Indeed, the directions of the sprite are 0, 1, 2 and 3. So we have to multiply these directions by 2 to have the correct ones.
Then we can define the enemy:on_obstacle_reached() event, the most interesting one.
We can divide it into three parts :
- Testing if the monster can go where it should (i.e. no obstacle on its right)
- Define the monster's direction
- Restart its movement
For the first part, we'll need a complex condition such like this :
Code Select
if (next_direction == 2 and self:test_obstacles(16, 0)) -- If the monster was going up
or (next_direction == 0 and self:test_obstacles(0, 16)) -- Was going right
or (next_direction == 6 and self:test_obstacles(-16, 0)) -- Was going down
or (next_direction == 4 and self:test_obstacles(0, -16)) -- Was going left
then
If true, it means that there is an obstacle on the monster's right ; so it will have to go left instead.
Let's define the direction for the monster's left :
Code Select
-- Then go to the monster's left
next_direction = next_direction + 2
if next_direction > 7 then
next_direction = 0
end
As we don't want the monster to have a diagonal movement, we add 2 to the next_direction variable. We also have to "reset" it if it was equal to 6 : indeed, 6 + 2 = 8, but "8" isn't a good direction. So we restart it.
Now, let's define the "normal" monster's behavior :
Code Select
else -- The monster can go on its right
next_direction = next_direction - 2
if next_direction < 0 then
next_direction = 6
end
end
It's like the previous piece of code, but in the other way.
Finally, let's make the monster restart in the new next_direction :
Code Select
-- Set the direction and restart the movement
sprite:set_direction(next_direction / 2)
self:restart()
end
Note that you have to divide next_direction by 2 this time, for the same reason I was talking about some lines earlier.
Here's a tip. I previously said that the "pikeball" shall go back if it cannot go on this left nor on its right. But I didn't write anything about that case.
In fact, it's not necessary to write someting for it. Indeed, the first condition is quite efficient for that : it checks whether you can go on your right or not, and if not, the "pikeball" goes on its left. But if there's also something on the left, the enemy:on_obstacle_reached() event is activated again, and will test again if there is an obstacle on the right. And it will be the case since that obstacle is the one the "pikeball" hurt at first ; so its only choice will be left. And left + left = go back.
If you didn't understand what I said, don't worry, the most important thing to remember is that you don't have to bother with the "come back" case.
You can now insert your enemy in the quest editor and... enjoy !
Here is the complete .lua script for the fireball type enemy :
Code Select
local enemy = ...
-- Enemy that always moves, horizontally or vertically depending on its direction
-- When it hurts an obstacle, it goes on its right ; if impossible, goes to the left or come back
-- If you know Chip's Challenge, it's like the fireball's movement ^^
local sprite
local next_direction
-- Do whatever you want for this part
function enemy:on_created()
self:set_life(1)
self:set_damage(2)
sprite = self:create_sprite("enemies/pike_clockwise") -- But don't forget to define "sprite" and set a value
self:set_size(16, 16)
self:set_origin(8, 13)
self:set_can_hurt_hero_running(true)
self:set_invincible()
self:set_attack_consequence("sword", "protected")
self:set_attack_consequence("thrown_item", "protected")
self:set_attack_consequence("arrow", "protected")
self:set_attack_consequence("hookshot", "protected")
self:set_attack_consequence("boomerang", "protected")
end
-- In this part, we define the straight movement
function enemy:on_restarted()
next_direction = sprite:get_direction() * 2 -- Don't forget the "* 2" factor
local m = sol.movement.create("path")
m:set_path{next_direction}
m:set_speed(128)
m:set_loop(true)
m:start(self)
end
-- In this part, we define the monster behavior when it hurts an obstacle
function enemy:on_obstacle_reached()
-- Test : is there an obstacle on the monster's right ?
if (next_direction == 2 and self:test_obstacles(16, 0)) -- If the monster was going up
or (next_direction == 0 and self:test_obstacles(0, 16)) -- Was going right
or (next_direction == 6 and self:test_obstacles(-16, 0)) -- Was going down
or (next_direction == 4 and self:test_obstacles(0, -16)) -- Was going left
then
-- Then go to the monster's left
next_direction = next_direction + 2
if next_direction > 7 then
next_direction = 0
end
else -- The monster can go on its right
next_direction = next_direction - 2
if next_direction < 0 then
next_direction = 6
end
end
-- Set the direction and restart the movement
sprite:set_direction(next_direction / 2)
self:restart()
end
So much for the fireball type.
You can do the glider in order to practice. You'll just have to switch the directions in the enemy:on_obstacle_reached() event, it's as easy as pie.