1.14.11 - Enemy

An enemy is a bad guy that hurts the hero when touching him.

This type of map entity can be declared in the map data file. It can also be created dynamically with map:create_enemy().

Overview

Enemies can exist in various breeds. Each breed corresponds to a model of enemy with its behavior, its sprites and its movements.

The script file enemies/XXXX.lua defines the enemy breed XXXX. This script is executed every time an enemy of that model is created. The corresponding Lua enemy object is passed as parameter of that script. Use the Lua notation "..." to get this parameter and store it into a regular variable.

Here is a basic example of script for the enemy breed "tentacle", a simple model of enemy that just follows the hero.

-- First, we put the parameter into a variable called "enemy".
-- (In Lua, the notation "..." refers to the parameter(s) of the script.)
local enemy = ...

-- Called when the enemy was just created on the map.
function enemy:on_created()

  -- Define the properties of this enemy.
  self:set_life(1)
  self:set_damage(2)
  self:create_sprite("enemies/tentacle")
  self:set_size(16, 16)
  self:set_origin(8, 13)
end

-- Called when the enemy should start or restart its movement
-- (for example if the enemy has just been created or was just hurt).
function enemy:on_restarted()

  -- Create a movement that walks toward the hero.
  local m = sol.movement.create("target")
  m:set_speed(32)
  m:start(self)
end

Such a script is all what you need to define a model of enemy. The engine handles for you the detection of collisions with the hero or his weapons, hurts the hero or the enemy when appropriate, removes the enemy when it gets killed, etc. But you can customize everything using the API described on this page, like what kind of attacks can hurt the enemy. You can also make complex enemies composed of several sprites, and set different behavior to each sprite. This is very useful to program a boss.

Timers are handy to script some repeated behavior on enemies, like performing a particular attack every 5 seconds. Create your timers from the enemy:on_restarted() event. Timers of an enemy are automatically destroyed when the enemy is hurt or immobilized, so that they don't get triggered during these special states. They are also destroyed when you call enemy:restart(). When enemy:on_restarted() is called, you are guaranteed that no timers exist on your enemy. Thus, it is safe to create them there.

Basic enemies often have the same behavior. To avoid duplication of code, you can factorize some code into a generic file and call it from each enemy breed script with require(), sol.main.do_file() or sol.main.load_file().

Methods inherited from map entity

Enemies are particular map entities. Therefore, they inherit all methods from the type map entity.

See Methods of all entity types to know these methods.

Methods of the type enemy

The following methods are specific to enemies.

enemy:get_breed()

Returns the breed (the model) of this enemy.

enemy:get_life()

Returns the current life of this enemy.

enemy:set_life(life)

Sets the current life of this enemy.

The initial value is 1.

enemy:add_life(life)

Adds some life to the enemy.

Remarks
Equivalent to enemy:set_life(enemy:get_life() + life)

enemy:remove_life(life)

Removes some life from the enemy.

Remarks
Equivalent to enemy:set_life(enemy:get_life() - life)

enemy:get_damage()

Returns the number of life points that the enemy removes from the hero when touching him. This number will be divided by the level of resistance ability of the player (his tunic).

enemy:set_damage(damage)

Sets the number of life points that the enemy removes from the hero when touching him. This number will be divided by the tunic level of the player, unless you override this default calculation in hero:on_taking_damage().

The default value is 1.

enemy:is_pushed_back_when_hurt()

Returns whether the enemy is pushed away when it is hurt.

enemy:set_pushed_back_when_hurt([pushed_back_when_hurt])

Sets whether the enemy should be pushed away when it is hurt.

The default value is true.

enemy:get_push_hero_on_sword()

Returns whether the hero is pushed away when he hits this enemy with his sword.

enemy:set_push_hero_on_sword([push_hero_on_sword])

Sets whether the hero should be pushed away when he hits this enemy with his sword.

The default value is false.

enemy:get_can_hurt_hero_running()

Returns whether this enemy can hurt the hero even when the hero is running.

enemy:set_can_hurt_hero_running([can_hurt_hero_running])

Sets whether this enemy can hurt the hero even when the hero is running.

The default value is false.

enemy:get_hurt_style()

Returns the style of sounds and animations to play when this enemy is hurt.

enemy:set_hurt_style(hurt_style)

Sets the style of sounds and animations to play when this enemy is hurt. The default values is "normal".

enemy:get_can_attack()

Returns whether this enemy can currently attack the hero.

enemy:set_can_attack([can_attack])

Sets whether this enemy can currently attack the hero.

When the enemy restarts after being hurt, can_attack is always set to true.

enemy:get_minimum_shield_needed()

Returns the level of protection (if any) that stops attacks from this enemy.

If the player has a protection ability greater than or equal to this value, he will stop attacks from this enemy if he is facing the direction of the enemy. The special value of 0 means that attacks cannot be stopped with the protection ability. Returns the required level of protection to stop attacks from this enemy.

enemy:set_minimum_shield_needed(minimum_shield_needed)

Sets a level of protection that stops attacks from this enemy.

If the player has a protection ability greater than or equal to this value, he will stop attacks from this enemy if he is facing the direction of the enemy. The special value of 0 means that attacks cannot be stopped with the protection ability. The default value is 0.

enemy:is_traversable()

Returns whether this enemy can be traversed by other entities.

enemy:set_traversable([traversable])

Sets whether this enemy can be traversed by other entities.

By default, the enemy is traversable. For example, if you want to prevent the hero to pass without killing the enemy, you can use this function to make the enemy become an obstacle.

Remarks
When the enemy is not traversable, the enemy can no longer hurt the hero since their sprites cannot overlap anymore (unless if the enemy's sprite is bigger than its bounding box). You can use enemy:set_attacking_collision_mode("touching") to make the enemy still hurt the hero when touching him without overlapping.

enemy:get_attacking_collision_mode()

Returns the kind of collision test performed to detect when the hero should be hurt by this enemy.

enemy:set_attacking_collision_mode(collision_mode)

Sets the kind of collision test performed to detect when the hero should be hurt by this enemy.

enemy:get_attack_consequence(attack)

Returns how this enemy reacts when he receives an attack.

Recall that enemies may have several sprites. This attack consequence applies to all sprites of the enemy, unless you override some of them with enemy:set_attack_consequence_sprite().

enemy:set_attack_consequence(attack, consequence)

Sets how this enemy reacts when he receives an attack.

Recall that enemies may have several sprites. This attack consequence applies to all sprites of the enemy, unless you override some of them with enemy:set_attack_consequence_sprite().

enemy:get_attack_consequence_sprite(sprite, attack)

Returns how this enemy reacts when one of his sprites receives an attack.

This method returns the same result as enemy:get_attack_consequence(), unless you override the reaction of the enemy for a particular sprite with enemy:set_attack_consequence_sprite().

enemy:set_attack_consequence_sprite(sprite, attack, consequence)

Sets how this enemy reacts when one of his sprites receives an attack.

This method overrides for a particular sprite the attack consequences defined by enemy:set_attack_consequence().

enemy:set_default_attack_consequences()

Restores the default attack consequences for this enemy and its sprites.

enemy:set_default_attack_consequences_sprite(sprite)

Restores the default attack consequences for a particular sprite of this enemy.

enemy:set_invincible()

Makes this enemy ignore all attacks.

Equivalent to calling enemy:set_attack_consequence(attack, "ignored") for each attack.

enemy:set_invincible_sprite(sprite)

Makes a sprite of this enemy ignore all attacks.

Equivalent to calling enemy:set_attack_consequence(sprite, attack, "ignored") for each attack.

enemy:has_layer_independent_collisions()

Returns whether this enemy can detect collisions with entities even if they are not on the same layer.

By default, enemies can only have collisions with entities on the same layer.

enemy:set_layer_independent_collisions([independent])

Sets whether this enemy can detect collisions with entities even if they are not on the same layer.

By default, enemies can only have collisions with entities on the same layer. If you set this property to true, this enemy will be able to hurt the hero even from a different layer.

enemy:get_treasure()

Returns the pickable treasure that will drop this enemy when killed.

enemy:set_treasure([item_name, [variant, [savegame_variable]]])

Sets the pickable treasure that will drop this enemy when killed.

enemy:get_obstacle_behavior()

Returns how the enemy behaves with obstacles.

enemy:set_obstacle_behavior(obstacle_behavior)

Sets how this enemy should behave with obstacles. The default value is "normal". "swimming" allow the enemy to traverse water. "flying" allows the enemy to traverse holes, water and lava.

enemy:set_size(width, height)

Sets the size of the bounding box of this enemy.

The default value is 16x16 pixels. This is the effective size used to detect obstacles when moving, but the sprite of the enemy may be larger, especially for a boss.

Remarks
Collisions with the hero are pixel-precise and use the sprite of the enemy, not his bounding box. Therefore, this function has no influence on collisions with the hero, but only on the detection of obstacles of the map when the enemy moves.

enemy:set_origin(origin_x, origin_y)

Sets the origin point of this enemy, relative to the upper left corner of its bounding box.

This origin point property allows entities of different sizes to have comparable reference points that can be used by the engine. Indeed, when two enemies overlap, the engine needs to determine which one has to be displayed first (it is always the one with the lowest Y coordinate). Sometimes, the engine also needs to compute an angle between two entities, for example to push away an enemy that was just hit. Using the upper left corner of their bounding box would not give the correct angle (unless both entities had the same size).

The origin point is also the point of synchronization of an entity with its sprites (because again, an entity that has a given size may have sprites with different sizes).

The default values is 8,13 and is usually okay for enemies of size 16x16. See entity:get_origin() for more explanations about the origin point.

enemy:restart()

Restarts this enemy.

This plays animation "walking" on its sprites, destroys any timer of the enemy and calls the event enemy:on_restarted().

This function has no effect if the enemy is dying.

enemy:hurt(life_points)

Hurts this enemy if he is currently vulnerable.

If the enemy is vulnerable, the hurting animation and sound start and the given number of life points are removed.

Nothing happens if the enemy is currently invulnerable (for example because he is already being hurt).

Remarks
If you just want to silently remove some life, call enemy:remove_life() instead.

enemy:immobilize()

Immobilizes this enemy for a while if possible.

After a few seconds, the enemy shakes and then restarts.

enemy:create_sprite(animation_set_id, [sprite_name])

Creates a sprite for this enemy.

Remarks
If you don't create a sprite, your enemy will be invisible.

enemy:remove_sprite([sprite])

Removes and destroys a sprite of this enemy.

The sprite must have been created before by enemy:create_sprite().

enemy:create_enemy(properties)

Creates another enemy on the map, specifying its coordinates as relative to the current enemy.

This function is similar to map:create_enemy() but the coordinates are relative to the current enemy, and the layer is the one of the current enemy by default.

Events inherited from map entity

Events are callback methods automatically called by the engine if you define them.

Enemies are particular map entities. Therefore, they inherit all events from the type map entity.

See Events of all entity types to know these events.

Events of the type enemy

The following events are specific to enemies.

enemy:on_update()

Called at each cycle while this enemy is alive.

Remarks
As this function is called at each cycle, it is recommended to use other solutions when possible, like timers and other events.

enemy:on_suspended(suspended)

Called when the map has just been suspended or resumed.

The map is suspended by the engine in a few cases, like when the game is paused or when the camera is being moved by a script. When this happens, all map entities stop moving and most sprites stop their animation.

enemy:on_enabled()

called when this enemy has just been enabled.

enemy:on_disabled()

called when this enemy has just been disabled.

enemy:on_restarted()

Called when this enemy should start or restart its movement and timers because something happened. For example, the enemy has just been created, or it was just hurt or immobilized, or you called enemy:restart(). If your enemy should move, this is the right place to create its movement.

Timers associated to the enemy were automatically destroyed. Thus, you should also recreate them from this event.

enemy:on_pre_draw()

Called just before the enemy is drawn on the map. You may display additional things below the enemy.

enemy:on_post_draw()

Called just after the enemy is drawn on the map. You may display additional things above the enemy.

enemy:on_collision_enemy(other_enemy, other_sprite, my_sprite)

Called when a sprite of this enemy overlaps another enemy's sprite.

enemy:on_custom_attack_received(attack, sprite)

Called when this enemy receives an attack with a custom effect.

This function is called if you have set consequence of the attack to "custom". You have to define what happens, for example hurting the enemy, making a special reaction, etc.

enemy:on_hurt_by_sword(hero, enemy_sprite)

Called when this enemy is successfully hurt by the sword of the hero.

You should define this event to customize the damage inflicted by the sword.

This event can only be called if the reaction to the "sword" attack is hurting the enemy.

At this point, the enemy is in the state of being hurt. His hurting animation and sound have just started. This is a good time to remove some life points with enemy:remove_life().

By default, if you don't define this event, the enemy loses a number of life points computed as his reaction to sword attacks multiplied by the sword ability level of the hero, and doubled during a spin attack.

enemy:on_hurt(attack)

Called when this enemy is successfully hurt by any attack.

This event can only be called if the reaction to the attack is hurting the enemy.

At this point, the enemy is in the state of being hurt. His hurting animation and sound have just started and he has just lost some life.

Remarks
In the case of a "sword" attack, this event is called right after enemy:on_hurt_by_sword().

enemy:on_dying()

Called when the enemy's life comes to 0.

When the life comes to 0, the movement of the enemy is stopped, its timers are stopped too, the dying animation starts and a sound is played. The details of the dying animation and the sound played depend on the hurt style property. If the hurt style is "enemy" or "monster", any sprite you created on the enemy is automatically removed and replaced by the sprite "enemies/enemy_killed". If the hurt style is "boss", your sprites continue to exist and to play animation "hurt", while explosions appear on the enemy.

In both cases, the enemy will be removed from the map when the dying animation ends.

Remarks
This event is called right after enemy:on_hurt().

enemy:on_dead()

Called when the enemy's dying animation is finished.

At this point, the enemy no longer exists on the map. In other words, enemy:exists() returns false, trying to get the enemy from its name returns nil, and functions like map:get_entities(prefix) won't find this enemy.

This means that you can safely use map:has_entities(prefix) from enemy:on_dead() to detect when all enemies with a common prefix are dead.

enemy:on_immobilized()

Called when the enemy is immobilized.

enemy:on_attacking_hero(hero, enemy_sprite)

Called when the hero is successfully touched by this enemy.

This event is not called if the hero was protected by his shield, or if he currently cannot be hurt for some reason, like when he is already being hurt, when he is temporarily invincible, or when he is in a special state like brandishing a treasure.

Your script can define this event to customize what bad things happen to the hero. If you define this event, the engine does absolutely nothing and lets you handle this.

If you don't define this event, the hero is hurt with the predefined behavior as follows. The hero goes to the state "hurt" where is pushed away from the enemy. He loses some life depending on the enemy's damage property, and on the hero's tunic and on hero:on_taking_damage() if defined. Then, he recovers and his sprites blink for a while. During this short period, he is temporarily invincible.

Remarks
If you call hero:start_hurt(), you will obtain something equivalent to the default behavior. But if you don't, keep in mind that if the hero can still be hurt after your call, this event will continue to be called while there is a collision with the enemy. To avoid this, see for example hero:set_invincible() to make the hero temporarily invincible.