1.11 - Game

This module provides a datatype "game" that represents a savegame.

Overview

Saved data

On a game object, you can access and modify everything that is saved. The data saved and restored by the engine are the following:

When a game is running, more features are available (like pausing the game, handling game commands, etc.). Only one game can be running at a time.

Game commands

An important concept that comes with the game is the notion of game commands. Game commands are built-in game actions that can be mapped to a low-level keyboard or joypad input. They can be seen as an abstraction of the keyboard and the joypad. Game commands are like a virtual game device that provides the the following buttons:

Of course, these virtual commands are mapped to real, low-level inputs from the keyboard and/or the joypad. You script can control which keyboard and joypad inputs are associated to each game command.

When a game command is pressed, no matter if the command came from the keyboard or the joypad, the engine performs some built-in behavior by default (like pausing the game or moving the hero to the right). But you can also extend or override this behavior, because the engine notifies you first (see game:on_command_pressed()). Therefore, you usually don't have to worry about the underlying keyboard or joypad input (but if you want to, you can, and the callbacks are exactly the sames as in sol.main and in menus).

Accessing the game like tables

Objects of the game type are userdata, but like most Solarus Lua types, they can also be accessed like tables. This is especially useful for the game type to add, next to the built-in game features, your own quest-specific functions and data (like your HUD and your pause menu).

Functions of sol.game

sol.game.exists(file_name)

Returns whether the specified savegame file exists.

A valid quest write directory must be set (in your quest.dat file or by calling sol.main.set_quest_write_dir()), otherwise savegames cannot be used and this function generates a Lua error.

sol.game.delete(file_name)

Deletes a savegame file.

A valid quest write directory must be set (in your quest.dat file or by calling sol.main.set_quest_write_dir()), otherwise savegames cannot be used and this function generates a Lua error.

sol.game.load(file_name)

Loads an existing savegame, or initializes a new one if it does not exist (but does not save it).

A valid quest write directory must be set (in your quest.dat file or by calling sol.main.set_quest_write_dir()), otherwise savegames cannot be used and this function generates a Lua error.

Remarks
This function does not start the game, it just loads the savegame file and initializes all equipment item scripts. Then you can access the data saved in the savegame file and use the API of equipment items. To actually run the game, call game:start().

Methods of the type game

game:save()

Saves this game into its savegame file.

A valid quest write directory must be set (in your quest.dat file or by calling sol.main.set_quest_write_dir()), otherwise savegames cannot be used and this function generates a Lua error.

game:start()

Runs this game.

This function is typically called from your savegame menu, when the player chooses its savegame file.

If another game was running, it is stopped automatically because only one game can be running at a time.

You can also call this function to restart the current game itself, even if it was not saved recently (saved data will not be reset). This may be useful to restart the game after the game-over sequence.

game:is_started()

Returns whether this game is currently running.

Only one game can be running at a time.

game:is_suspended()

Returns whether this game is currently suspended.

The game is suspended when at least one of the following conditions is true:

 

game:set_suspended([suspended])

Suspends or unsuspends the game.

Note that the game is also automatically suspended by the engine in the following situations:

Therefore, if you call game:set_suspended(false) during one of these sequences, it will only take effect at the end of it.

Note
When the hero goes to another map, the game is automatically unsuspended after the map opening transition.

game:is_paused()

Returns whether this game is currently paused.

game:set_paused([paused])

Pauses or resumes the game explictly.

Note that by default, a built-in game command already exists to pause and unpause the game.

game:is_pause_allowed()

Returns whether the player can pause or unpause the game.

game:set_pause_allowed([pause_allowed])

Sets whether the player can pause or unpause the game.

Remarks
This function applies to the built-in pause command. Your script can still pause the game explicitly by calling game:set_paused().

game:is_dialog_enabled()

Returns whether this game is currently showing a dialog.

It does not matter whether the dialog is shown with the built-in, minimal dialog box or with your custom dialog box (see game:on_dialog_started()).

Only possible when the game is running.

game:start_dialog(dialog_id, [info], [callback])

Starts showing a dialog.

A dialog must not be already active. This function returns immediately, but you can provide a callback that will be executed when the dialog finishes. The game is suspended during the dialog, like when it is paused.

If the event game:on_dialog_started() is not defined, then the engine will show a default, minimal dialog system without decoration. The user will be able to close the dialog by pressing the action command (you don't need to call game:stop_dialog()).

On the contrary, if the event game:on_dialog_started() is defined, the engine calls it and does nothing else. This is the recommended way, because you can make your custom dialog box implementation with any feature you need. The game will be suspended until you call game:stop_dialog() explicitly.

Example of a small map script with an NPC that shows a simple dialog:

local map = ...

function some_npc:on_interaction()
  -- Remember that you specify a dialog id, not directly the text to show.
  -- The text is defined in the dialogs.dat file of the current language.
  map:get_game():start_dialog("welcome_to_my_house")
end

Here is a more complex example, with an NPC that asks a question. This example assumes that your dialog box system can ask questions to the player, and returns the answer as a boolean value passed to game:stop_dialog().

local map = ...
local game = map:get_game()
function another_npc:on_interaction()
  game:start_dialog("give_me_100_rupees_please", function(answer)
    if answer then
      if game:get_money() >= 100 then
        game:remove_money(100)
        game:start_dialog("thanks")
      else
        sol.audio.play_sound("wrong")
        game:start_dialog("not_enough_money")
      end
    else
      game:start_dialog("not_happy")
    end
  end)
end

Finally, to illustrate the use of the info parameter, let's modify the previous example to make the amount of money only determined at runtime. In other words, we want an NPC that can say "Please give me 100 rupees", but also "Please give me 25 rupees" or any number. Since the number is only known at runtime, it can no longer be hardcoded in the text of the dialog. So let's assume that the text of the dialog contains instead a special sequence (like "$v") to be substituted by the final value. (Note that shop treasures use a very similar convention for their dialogs.)

local map = ...
local game = map:get_game()

function another_npc:on_interaction()
  local how_much = math.random(100)
  game:start_dialog("give_me_x_rupees_please", how_much, function(answer)
    if answer then
      if game:get_money() >= how_much then
        game:remove_money(how_much)
        -- ... The rest is unchanged.

To make this example work, you need a dialog box system that performs the substitution when info is set. See game:on_dialog_started().

Note
The info parameter of game:start_dialog() and the status parameter of the callback are a flexible way to make the map script communicate with the dialog box system in both directions. They can been seen as the parameter and the result (respectively) of the dialog being displayed. They can both be any value, like a table with many information.

game:stop_dialog([status])

Stops the current dialog.

A dialog must be active when you call this function. The dialog stops being displayed and the game can resume. This function is typically called by your dialog box system when it wants to close the dialog.

The game:on_dialog_finished() event is first called (if it is defined). Then, the callback that was passed to game:start_dialog() is called (if it was defined) with the status argument.

game:is_game_over_enabled()

Returns whether this game is currently showing a game-over sequence.

Only possible when the game is running.

The game-over sequence automatically starts when the player's life gets to zero, or when you call game:start_game_over() explicitly. Define the event game:on_game_over_started() to show your game-over menu. If you don't define this event, by default, there is no game-over sequence and the engine immediately restarts the game (but does not save it).

game:start_game_over()

Starts the game-over sequence manually.

Only possible when the game is running.

This function is seldom needed since the game-over sequence automatically starts when the player's life reaches zero. But you can use it if you want to start a game-over sequence even when the player's life is greater than zero.

game:stop_game_over()

Finishes the current game-over sequence.

Only possible during a game-over sequence.

The game is suspended during the whole game-over sequence. Call this function to resume it. If the life is still zero at this point, then the engine automatically restores full life.

game:get_map()

Returns the current map.

game:get_hero()

Returns the hero.

The hero is a map entity that always exists while the game is running, and that persists when the map changes. For this reason, he can be seen as belonging to the game more than to the current map. That's why this function exists.

Remarks
Equivalent to game:get_map():get_entity("hero").

game:get_value(savegame_variable)

Returns a value saved.

game:set_value(savegame_variable, value)

Sets a value in the savegame.

This function allows to store key-value pairs in the savegame. Values can be strings, integers or booleans.

Remarks
This method changes a value, but remember that the change will be saved in the savegame file only when you call game:save().

game:get_starting_location()

Returns the location where the hero is placed when this game is started or restarted.

game:set_starting_location([map_id, [destination_name]])

Sets the location where the hero should be placed when this game is started or restarted.

Remarks
When the hero moves from a map to another map that belongs to a different world (for example, from a dungeon to the outside world) using a destination entity, by default, the starting location is automatically set to this point. If this behavior is okay for your quest, you never need to call this function except the first time: when initializing a new savegame file. This behavior can be changed by setting the "Save starting location" property of destinations, from the quest editor or from a script (with destination:set_starting_location_mode()).

game:get_life()

Returns the current level of life of the player.

game:set_life(life)

Sets the level of life of the player.

A negative value will be replaced by zero. A value greater than than the maximum level of life will be replaced by the maximum value.

game:add_life(life)

Adds some life to the player.

Remarks
Equivalent to game:set_life(game:get_life() + life).

game:remove_life(life)

Removes some life from the player.

Remarks
Equivalent to game:set_life(game:get_life() - life).

game:get_max_life()

Returns the maximum level of life of the player.

game:set_max_life(life)

Sets the maximum level of life of the player.

game:add_max_life(life)

Increases the maximum level of life of the player.

Remarks
Equivalent to game:set_max_life(game:get_max_life() + life).

game:get_money()

Returns the amount of money of the player.

game:set_money(money)

Sets the amount of money of the player.

A negative value will be replaced by zero. A value greater than than the maximum amount of money will be replaced by the maximum amount.

game:add_money(money)

Adds some money to the player.

Remarks
Equivalent to game:set_money(game:get_money() + money).

game:remove_money(money)

Removes some money from the player.

Remarks
Equivalent to game:set_money(game:get_money() - money).

game:get_max_money()

Returns the maximum amount of money of the player.

game:set_max_money(money)

Sets the maximum amount of money of the player.

game:get_magic()

Returns the current number of magic points.

game:set_magic(magic)

Sets the amount of magic points of the player.

A negative value will be replaced by zero. A value greater than than the maximum number of magic points will be replaced by that maximum.

game:add_magic(magic)

Adds some magic points to the player.

Remarks
Equivalent to game:set_magic(game:get_magic() + magic).

game:remove_magic(magic)

Removes some magic points from the player.

Remarks
Equivalent to game:set_magic(game:get_magic() - magic).

game:get_max_magic()

Returns the maximum number of magic points.

game:set_max_magic(magic)

Sets the maximum number of magic points.

game:has_ability(ability_name)

Returns whether the player has a built-in ability.

Remarks
Equivalent to game:get_ability(ability_name) > 0.

game:get_ability(ability_name)

Returns the level of a built-in ability.

Built-in ability levels indicate whether the hero can perform some built-in actions like attacking, swimming or running. The initial value is 0 unless specified otherwise.

game:set_ability(ability_name, level)

Sets the level of an ability.

game:get_item(item_name)

Returns an equipment item.

game:has_item(item_name)

Returns whether the player has the specified equipment item (only for a saved item).

Remarks
Equivalent to game:get_item(item_name):get_variant() > 0.

game:get_item_assigned(slot)

Returns the equipment item assigned to a slot.

game:set_item_assigned(slot, item)

Assigns an equipment item to a slot.

game:get_command_effect(command)

Returns the current built-in effect of a game command.

This function is useful if you want to show a HUD that indicates to the player the current effect of pressing a game command, especially for command "action" whose effect changes a lot depending on the context.

Remarks
All these built-in game commands are initially mapped to some default keyboard and joypad inputs. You can use game:set_command_keyboard_binding(), game:set_command_joypad_binding() and game:capture_command_binding() to change or even disable these mappings.
It is also possible to override the behavior of game commands by intercepting the events game:on_command_pressed() game:on_command_released().

game:get_command_keyboard_binding(command)

Returns the keyboard key that triggers the specified game command.

game:set_command_keyboard_binding(command, key)

Sets the keyboard key that triggers a game command.

Note
If this keyboard key was already mapped to a command, keyboard keys of both commands are switched.

game:get_command_joypad_binding(command)

Returns the joypad input that triggers the specified game command.

game:set_command_joypad_binding(command, joypad_string)

Sets the joypad input that should trigger the specified game command.

Note
If this joypad input was already mapped to a command, joypad inputs of both commands are switched.

game:capture_command_binding(command, [callback])

Makes the next keyboard or joypad input become the new binding for the specified game command.

This function returns immediately. After you call it, the next time the player presses a keyboard key or performs a joypad input, this input is treated differently: instead of being forwarded to your script or handled by the engine as usual, it automatically becomes the new keyboard or joypad binding for a game command.

Note
If the keyboard (or joypad) input was already mapped to a command, keyboard (or joypad) inputs of both commands are switched.

game:is_command_pressed(command)

Returns whether a built-in game command is currently pressed.

game:get_commands_direction()

Returns the direction (in an 8-direction system) formed by the combination of directional game commands currently pressed by the player.

Remarks
This function is provided for convenience. Its result can also be computed by calling game:is_command_pressed() four times (with the four directional game commands).

game:simulate_command_pressed(command)

Creates a command pressed input event.

Everything acts like if the player had just pressed an input mapped to this game command.

game:simulate_command_released(command)

Creates a command released input event.

Everything acts like if the player had just released an input mapped to this game command.

Events of a game

Events are callback methods automatically called by the engine if you define them. In the case of a game, they are only called on the game currently running, if any.

game:on_started()

Called when this game starts running (including when you restart the same game).

game:on_finished()

Called when this game stops running (including when you restart the same game).

game:on_update()

Called at each cycle of the main loop while this game is running.

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

game:on_draw(dst_surface)

Called when the game has just been redrawn by the engine.

The engine has already drawn the current map, since the map is always drawn before the game. If the game has menus, these menu are not drawn yet at this point. Use this event if you want to draw some additional content before the menus.

game:on_map_changed(map)

Called when the player has just entered a map.

The new map is already started at this point. For example, you may use this event if some parts of your HUD needs to be changed on particular maps.

Remarks
This event is also called for the first map (when your game starts).

game:on_paused()

Called when the game has just been paused.

The game may have been paused by the player (by pressing the "pause" game command) or by you (by calling game:set_paused(true)).

This function is typically the place where you should start your pause menu.

game:on_unpaused()

Called when the game is being resumed.

The game may have been unpaused by the player (by pressing the "pause" game command) or by you (by calling game:set_paused(false)).

This is probably a good place to stop your pause menu.

game:on_dialog_started(dialog, [info])

Called when a dialog starts.

The dialog may be triggered by a Lua script (by calling game:start_dialog()) or by the engine in various situations (for example when finding a treasure).

If this event is not defined, the engine shows a minimal dialog box without decoration and you have nothing else to do.

If this event is defined, the engine does nothing and your script is responsible to show the dialog in any way you want, and to close it later by calling game:stop_dialog(). It is recommended to implement your dialog system as a menu: if you do so, you will automatically get called by the engine when a command is pressed, when you need to draw the dialog box on the screen, etc.

game:on_dialog_finished(dialog)

Called when the current dialog stops.

game:on_game_over_started()

Called when a game-over sequence starts.

This event is called when the player's life reaches zero, as soon as the hero is in a state that allows game-over. It is also called if you started a game-over sequence manually with game:start_game_over().

If this event is not defined, there is no game-over sequence: the game restarts immediately, like if you called game:start(), and the full life of the player is restored.

If this event is defined, the engine does nothing except suspending the game. Your script is then responsible to show a game-over sequence in any way you want, and to call game:stop_game_over() once you have finished.

For instance, you may create a dialog that lets the player restart the game or save and quit, or a menu with more options.

Actually, it is not even required to restart or quit the game after your game-over sequence (even if this is the most common case). Indeed, you can also just resume the game. In this case, the game continues normally like if nothing happened.

game:on_game_over_finished()

Called when the current game-over sequence stops.

This event is also called if you did not define a game-over sequence.

game:on_key_pressed(key, modifiers)

Called when the user presses a keyboard key while your game is running.

If you handle the event, you should return true to make the event stop being propagated. The menus of your game (if any) and the current map won't be not notified in this case. On the contrary, if neither your game, its menus nor the current map handle the event, then the engine handles it with a built-in behavior. This built-in behavior is to check whether a game command is mapped to the keyboard key that was pressed. If yes, the keyboard pressed event will be transformed into a game command pressed event (see game:on_command_pressed()).

Remarks
This event indicates the raw keyboard key pressed. If you want the corresponding character instead (if any), see game:on_character_pressed(). If you want the corresponding higher-level game command (if any), see game:on_command_pressed().

game:on_key_released(key, modifiers)

Called when the user releases a keyboard key while your game is running.

If you handle the event, you should return true to make the event stop being propagated. The menus of your game (if any) and the current map won't be not notified in this case. On the contrary, if neither your game, its menus nor the current map handle the event, then the engine handles it with a built-in behavior. This built-in behavior is to check whether a game command is mapped to the keyboard key that was released. If yes, the "keyboard released" event will be transformed into a "game command released" event (see game:on_command_released()).

game:on_character_pressed(character)

Called when the user enters text while your game is running.

Remarks
When a character key is pressed, two events are called: game:on_key_pressed() (indicating the raw key) and game:on_character_pressed() (indicating the utf-8 character). If your game needs to input text from the user, game:on_character_pressed() is what you want because it considers the keyboard's layout and gives you international utf-8 strings.

game:on_joypad_button_pressed(button)

Called when the user presses a joypad button while your game is running.

game:on_joypad_button_released(button)

Called when the user releases a joypad button while your game is running.

game:on_joypad_axis_moved(axis, state)

Called when the user moves a joypad axis while your game is running.

game:on_joypad_hat_moved(hat, direction8)

Called when the user moves a joypad hat while your game is running.

game:on_command_pressed(command)

Called when the player presses a game command (a keyboard key or a joypad action mapped to a built-in game behavior) while this game is running. You can use this event to override the normal built-in behavior of the game command.

Remarks
This event is not triggered if you already handled its underlying low-level keyboard or joypad event.

game:on_command_released(command)

Called when the player released a game command (a keyboard key or a joypad action mapped to a built-in game behavior). while this game is running. You can use this event to override the normal built-in behavior of the game command.

Remarks
This event is not triggered if you already handled its underlying low-level keyboard or joypad event.

game:on_mouse_pressed(button, x, y)

Called when the user presses a mouse button while the game is running.

game:on_mouse_released(button, x, y)

Called when the user releases a mouse button while the game is running.

game:on_finger_pressed(finger, x, y, pressure)

Called when the user presses a finger while the game is running.

game:on_finger_released(finger, x, y, pressure)

Called when the user releases a finger while the game is running.

game:on_finger_moved(finger, x, y, dx, dy, pressure)

Called when the user moves a finger while the game is running.