Difference between revisions of "Characters"

From Wolfire Games Wiki
Jump to: navigation, search
m (How to add a character to the spawner menu)
(Add initial documentation for most basic script functions)
Line 93: Line 93:
  
 
See '''<code>GameInstallDir/Data/ExampleMods/mod_xml_specification.txt</code>''' for full information.
 
See '''<code>GameInstallDir/Data/ExampleMods/mod_xml_specification.txt</code>''' for full information.
 +
 +
= Character script hook functions =
 +
 +
These functions are '''required'''. If you override the character controller script for a character in your mod, you must include all of these functions:
 +
 +
* [[#Init function|<code>bool Init(string character_path) { return true; }</code>]]
 +
* [[#Reset function|<code>void Reset() { }</code>]]
 +
* [[#SetParameters function|<code>void SetParameters() { }</code>]]
 +
* [[#PostReset function|<code>void PostReset() { }</code>]]
 +
* [[#ScriptSwap function|<code>void ScriptSwap() { }</code>]]
 +
* [[#Update function|<code>void Update(int num_frames) { }</code>]]
 +
* [[#UpdatePaused function|<code>void UpdatePaused() { }</code>]]
 +
* [[#ReceiveMessage function|<code>void ReceiveMessage(string message) { }</code>]]
 +
* [[#SetEnabled function|<code>void SetEnabled(bool is_enabled) { }</code>]]
 +
* [[#MovementObjectDeleted function|<code>void MovementObjectDeleted(int character_id) { }</code>]]
 +
* [[#AttachWeapon function|<code>void AttachWeapon(int weapon_item_id) { }</code>]]
 +
* [[#AttachMisc function|<code>void AttachMisc(int misc_item_id) { }</code>]]
 +
* [[#HandleEditorAttachment function|<code>void HandleEditorAttachment(int item_id, int attachment_type, bool is_mirrored) { }</code>]]
 +
* [[#NotifyItemDetach function|<code>void NotifyItemDetach(int item_id) { }</code>]]
 +
* [[#GetTempHealth function|<code>float GetTempHealth() { return 1.0f; }</code>]]
 +
* [[#ForceApplied function|<code>void ForceApplied(vec3 force) { }</code>]]
 +
* [[#Contact function|<code>void Contact() { }</code>]]
 +
* [[#Collided function|<code>void Collided(float posx, float posy, float posz, float impulse, float hardness) { }</code>]]
 +
* [[#AboutToBeHitByItem function|<code>int AboutToBeHitByItem(int item_id) { return 1; }</code>]]
 +
* [[#HitByItem function|<code>void HitByItem(string material, vec3 point_of_impact, int item_id, int impact_type) { }</code>]]
 +
* [[#WasHit function|<code>int WasHit(string type, string attack_path, vec3 dir, vec3 pos, int attacker_id, float attack_damage_mult, float attack_knockback_mult) { return 1; }</code>]]
 +
* [[#HandleCollisionsBetweenTwoCharacters function|<code>void HandleCollisionsBetweenTwoCharacters([email protected] other_character) { }</code>]]
 +
* [[#PreDrawCamera function|<code>void PreDrawCamera(float curr_game_time) { }</code>]]
 +
* [[#PreDrawCameraNoCull function|<code>void PreDrawCameraNoCull(float curr_game_time) { }</code>]]
 +
* [[#PreDrawFrame function|<code>void PreDrawFrame(float curr_game_time) { }</code>]]
 +
* [[#WasBlocked function|<code>void WasBlocked() { }</code>]]
 +
 +
These functions are '''optional'''. These functions do not have to be added to your overridden character controller script, but will be called if they are present:
 +
 +
* [[#Dispose function|<code>void Dispose() { }</code>]]
 +
* [[#ResetWaypointTarget function|<code>void ResetWaypointTarget() { }</code>]]
 +
 +
== Init function ==
 +
 +
<pre style="white-space: pre-wrap;">bool Init(string character_path) { return true; }</pre>
 +
 +
[[#Character script hook functions|'''Required''']]
 +
 +
<code>Init</code> is called when the character is first to be loaded, upon level load.
 +
 +
Must return '''<code>true</code>''' upon success, and '''<code>false</code>''' upon failure.
 +
 +
Important things to do in this call are to set the motion object character path, load the character (<code>character_getter.Load(character_path);</code>), read the species tag (<code>character_getter.GetTag("species")</code>), set up the skeleton, etc.
 +
 +
Be careful, this may be called before some objects or script params are present in the level.
 +
 +
== Reset function ==
 +
 +
<pre style="white-space: pre-wrap;">void Reset() { }</pre>
 +
 +
[[#Character script hook functions|'''Required''']]
 +
 +
'''TODO''': How much of this is correct?
 +
 +
<code>Reset</code> is called whenever the level is reset. This does ''not'' happen when a level is first loaded, or when undo/redo are fired in the editor.
 +
 +
This happens when the player hits the "L" debug key to manually reset the level.
 +
 +
This also happens upon player respawn (but this is up to the level script, and modded level scripts might not do that).
 +
 +
When Reset gets called for a character:
 +
 +
# [[#Reset function|<code>Reset();</code>]] is called
 +
# Physics for the character is set up
 +
# Global variables in the AS context are reset
 +
# [[#SetParameters function|<code>SetParameters();</code>]] is called
 +
# [[#PostReset function|<code>PostReset();</code>]] is called
 +
# Item attachements are done, and attachment callbacks are called ([[#HandleEditorAttachment function|<code>[1]</code>]], [[#AttachWeapon function|<code>[2]</code>]], [[#AttachMisc function|<code>[3]</code>]])
 +
 +
== SetParameters function ==
 +
 +
<pre style="white-space: pre-wrap;">void SetParameters() { }</pre>
 +
 +
[[#Character script hook functions|'''Required''']]
 +
 +
<code>SetParameters</code> is called when the character is first loaded, whenever the script parameter values are changed via the GUI, and whenever [[#Reset function|<code>Reset()</code>]] is called.
 +
 +
It is also called when an undo/redo is fired in the editor, to reload script params from that point in history.
 +
 +
This function allows the character script to work with script parameter values.
 +
You can use it to define, change, verify, protect (undo writes), or locally cache script parameter values.
 +
 +
You can also use this to work with game objects that you use character script params to control (if any - this is a trick usually done in hotspot scripts, with placeholder objects, but there isn't any reason why a character script couldn't do similar things, if it was somehow valuable).
 +
 +
== PostReset function ==
 +
 +
<pre style="white-space: pre-wrap;">void PostReset() { }</pre>
 +
 +
[[#Character script hook functions|'''Required''']]
 +
 +
'''TODO''': How much of this is correct?
 +
 +
<code>PostReset</code> is called whenever the level is reset. This does ''not'' happen when a level is first loaded, or when undo/redo are fired in the editor.
 +
 +
This happens when the player hits the "L" debug key to manually reset the level.
 +
 +
This also happens upon player respawn (but this is up to the level script, and modded level scripts might not do that).
 +
 +
When Reset gets called for a character:
 +
 +
# [[#Reset function|<code>Reset();</code>]] is called
 +
# Physics for the character is set up
 +
# Global variables in the AS context are reset
 +
# [[#SetParameters function|<code>SetParameters();</code>]] is called
 +
# [[#PostReset function|<code>PostReset();</code>]] is called
 +
# Item attachements are done, and attachment callbacks are called ([[#HandleEditorAttachment function|<code>[1]</code>]], [[#AttachWeapon function|<code>[2]</code>]], [[#AttachMisc function|<code>[3]</code>]])
 +
 +
== ScriptSwap function ==
 +
 +
<pre style="white-space: pre-wrap;">void ScriptSwap() { }</pre>
 +
 +
[[#Character script hook functions|'''Required''']]
 +
 +
<code>ScriptSwap</code> is called before [[#Update function|<code>Update();</code>]], when the character control script gets swapped (caused by going from player control to AI control, or vice-versa).
 +
 +
This function gives the script an opportunity to properly adjust its variables after the transition to the new script, so updates happen smoothly.
 +
 +
'''Note''': The character model, rigged object, etc, stay loaded when this script is swapped, so this is mostly just to handle movement continuity (e.g. <code>last_col_pos = this_mo.position</code>, as in the aschar.as script), or special on-swap behavior (if you want to add any).
 +
 +
'''Note''': The update frequency is based on whether the new script is a player control script (<code>Data/Scripts/playercontrol.as</code>, set to update 120-per-second) or an AI control script (any other script, set to update 30-per-second, with a random offset with 120hz frequency to spread out the updates). This is why discontinuities may be good to correct for (''TODO''': Correct?).
 +
 +
When the script gets swapped:
 +
 +
# The global variables are saved from the old script
 +
# The new script is loaded
 +
# The global variables are restored to the new script
 +
# The update frequency gets selected, based on which script got loaded
 +
# [[#ScriptSwap function|this function is called]]
 +
 +
== Update function ==
 +
 +
<pre style="white-space: pre-wrap;">void Update() { }</pre>
 +
 +
[[#Character script hook functions|'''Required''']]
 +
 +
<code>Update</code> is called regularly by the engine so you can perform work.
 +
 +
It may be useful to do initialization once in this function, if you need objects or character script params to be present.
 +
 +
Be careful to keep this function short, sweet, and fast. <code>Update</code> gets called 120 times a second as of the time of this writing.
 +
 +
'''Note''': The update frequency is based on whether this script is a player control script (<code>Data/Scripts/playercontrol.as</code>, set to update 120-per-second) or an AI control script (any other script, set to update 30-per-second, with a random offset with 120hz frequency to spread out the updates).
 +
 +
== ReceiveMessage function ==
 +
 +
<pre style="white-space: pre-wrap;">void ReceiveMessage(string message) { }</pre>
 +
 +
[[#Character script hook functions|'''Required''']]
 +
 +
<code>ReceiveMessage</code> is called when messages have been sent to this character by the engine, level, or objects in levels.
 +
 +
Objects in levels (such as hotspots or other characters) can send this using <code>movement_object.SendMessage("some message string");</code>.
 +
 +
Parameters can be sent by separating them with spaces, and putting quotes around parameters that might contain spaces, then using the <code>TokenIterator</code> object. '''TODO''': Write a TokenIterator example and link to it.
 +
 +
'''Note''': If you're setting up the code to send a message to a character, you might want to choose to only send messages to player characters, or non-player characters. '''TODO''': Example of how to do this filtering.
 +
 +
== SetEnabled function ==
 +
 +
<pre style="white-space: pre-wrap;">void SetEnabled(bool is_enabled) { }</pre>
 +
 +
[[#Character script hook functions|'''Required''']]
 +
 +
[[#SetEnabled function|The <code>SetEnabled</code> hook function]] is called when <code>movement_object.SetEnabled(bool);</code> gets called on the character by another script.
 +
 +
This function allows the character to enable or disable any objects that should be associated with the character (such as fire ribbons, sounds, shadow objects, character camera item flashes, and water splashes).
 +
 +
This function can also be used to internally set variables so that its functions have no effect. The engine will generally not call the character if it is not enabled (except when it gets re-enabled), but other scripts could theoretically call functions on the character using <code>movement_object_instance.Execute("some code");</code>.
 +
 +
'''Note''': It may be viable to call [[#Dispose function|<code>Dispose()</code>]] from inside this function, as long as you're careful not to double-delete things.

Revision as of 01:19, 30 November 2017


Overview

Characters are a game entities that are directly controllable by a player or by the AI.

Characters have Rigged Objects attached, which support skeletal animation, rag doll animation, blends of those two, and character physics.

TODO: More in depth description, akin to that in Hotspots and LevelScripts

Documentation

Callable functions and data available inside character scripts

A list of functions, data, etc, is created automatically whenever you run the game, in a file on your hard drive. This lists what external code you get "for free" and can call from inside a character script.

  • Windows: My Documents\Wolfire\Overgrowth\aschar_docs.h
  • Mac: ~/Library/Application Support/Overgrowth/aschar_docs.h
  • Linux: ~/.local/share/Overgrowth/aschar_docs.h

This documentation will change with each version of the game, so keep checking back on this aschar_docs.h file to see the most up to date information.

There are also wiki pages which have more detailed documentation many of these functions. These pages have a danger of going out of date, but give more detailed documentation on some of the code:

Character instances in other scripts

Characters are exposed to other scripts as instances of the MovementObject class.

MovementObject instances are like other objects in the game. They support everything that is supported by Object, and also have their own special set of functions, properties, etc.

If you have a handle to a [email protected] object in angelscript, you can convert it to an [email protected] by calling ReadObjectFromID(movement_object.GetID());.

Working with Characters in editor

TODO: How to set a character as playable in the editor.

TODO: List of supported character parameters and how to use them. Some of these things verge more into "game" territory and not "engine" territory, so should at the very least mention that it's fully changeable in script...

TODO: List of species specific traits? Some of these things verge more into "game" territory and not "engine" territory, so should at the very least mention that it's fully changeable in script...

How to create a character

Character XML files

TODO: Description of XML format

File path conventions

TODO: Description of where to add files in mods so they don't conflict. Ala Hotspots#File_path_conventions

Modeling

TODO: Links to or docs on modeling, graphics features like fur fins, various texture channels and what their components (alpha etc) mean, UVs, object space normals, etc. Also, any export info that would be necessary.

Rigging

TODO: Links to or docs on rigging and rig export

Animations

TODO: Links to or docs on animations and export, tagging, etc

Attacks

TODO: Links to or docs on attacks system and export, tagging, etc - related partly to animations.

TODO: Possibly also scripting attacks and animations, and character-bound input, etc - though maybe that should be in a different section?

How to add a character to the spawner menu

Short version:

  • You must add it in a mod.
  • You can add as many characters to the spawner menu as you want in a single mod, you just need to follow the below instructions for each, and add multiple <Item> tags to your mod.xml file.

In your mod.xml file, add this xml tag: (TODO: Make these paths more specific to characters)

<Item category="My Custom Mod Characters"
      title="Some Character To Spawn"
      path="Data/Objects/example_item_pack/mod_item_example.xml"
      thumbnail="Data/UI/example_item_pack/thumbs/mod_item_example.png" />
  • category is the top level category where the character will show up, in the Load menu.
  • title is the name of the character, as it will show up in the spawner menu.
  • path is the path to the character XML that will get spawned. See the How to create a character section for which XML file to target (either the character XML itself, or a version you saved off that has modified default parameters). TODO: Be careful to advise which character XML to add to the spawner - there are multiple character XML files!
  • thumbnail is the image that will be used for a tooltip when you hover over your character in the spawner menu.

See GameInstallDir/Data/ExampleMods/mod_xml_specification.txt for full information.

Character script hook functions

These functions are required. If you override the character controller script for a character in your mod, you must include all of these functions:

These functions are optional. These functions do not have to be added to your overridden character controller script, but will be called if they are present:

Init function

bool Init(string character_path) { return true; }

Required

Init is called when the character is first to be loaded, upon level load.

Must return true upon success, and false upon failure.

Important things to do in this call are to set the motion object character path, load the character (character_getter.Load(character_path);), read the species tag (character_getter.GetTag("species")), set up the skeleton, etc.

Be careful, this may be called before some objects or script params are present in the level.

Reset function

void Reset() { }

Required

TODO: How much of this is correct?

Reset is called whenever the level is reset. This does not happen when a level is first loaded, or when undo/redo are fired in the editor.

This happens when the player hits the "L" debug key to manually reset the level.

This also happens upon player respawn (but this is up to the level script, and modded level scripts might not do that).

When Reset gets called for a character:

  1. Reset(); is called
  2. Physics for the character is set up
  3. Global variables in the AS context are reset
  4. SetParameters(); is called
  5. PostReset(); is called
  6. Item attachements are done, and attachment callbacks are called ([1], [2], [3])

SetParameters function

void SetParameters() { }

Required

SetParameters is called when the character is first loaded, whenever the script parameter values are changed via the GUI, and whenever Reset() is called.

It is also called when an undo/redo is fired in the editor, to reload script params from that point in history.

This function allows the character script to work with script parameter values. You can use it to define, change, verify, protect (undo writes), or locally cache script parameter values.

You can also use this to work with game objects that you use character script params to control (if any - this is a trick usually done in hotspot scripts, with placeholder objects, but there isn't any reason why a character script couldn't do similar things, if it was somehow valuable).

PostReset function

void PostReset() { }

Required

TODO: How much of this is correct?

PostReset is called whenever the level is reset. This does not happen when a level is first loaded, or when undo/redo are fired in the editor.

This happens when the player hits the "L" debug key to manually reset the level.

This also happens upon player respawn (but this is up to the level script, and modded level scripts might not do that).

When Reset gets called for a character:

  1. Reset(); is called
  2. Physics for the character is set up
  3. Global variables in the AS context are reset
  4. SetParameters(); is called
  5. PostReset(); is called
  6. Item attachements are done, and attachment callbacks are called ([1], [2], [3])

ScriptSwap function

void ScriptSwap() { }

Required

ScriptSwap is called before Update();, when the character control script gets swapped (caused by going from player control to AI control, or vice-versa).

This function gives the script an opportunity to properly adjust its variables after the transition to the new script, so updates happen smoothly.

Note: The character model, rigged object, etc, stay loaded when this script is swapped, so this is mostly just to handle movement continuity (e.g. last_col_pos = this_mo.position, as in the aschar.as script), or special on-swap behavior (if you want to add any).

'Note: The update frequency is based on whether the new script is a player control script (Data/Scripts/playercontrol.as, set to update 120-per-second) or an AI control script (any other script, set to update 30-per-second, with a random offset with 120hz frequency to spread out the updates). This is why discontinuities may be good to correct for (TODO: Correct?).

When the script gets swapped:

  1. The global variables are saved from the old script
  2. The new script is loaded
  3. The global variables are restored to the new script
  4. The update frequency gets selected, based on which script got loaded
  5. this function is called

Update function

void Update() { }

Required

Update is called regularly by the engine so you can perform work.

It may be useful to do initialization once in this function, if you need objects or character script params to be present.

Be careful to keep this function short, sweet, and fast. Update gets called 120 times a second as of the time of this writing.

Note: The update frequency is based on whether this script is a player control script (Data/Scripts/playercontrol.as, set to update 120-per-second) or an AI control script (any other script, set to update 30-per-second, with a random offset with 120hz frequency to spread out the updates).

ReceiveMessage function

void ReceiveMessage(string message) { }

Required

ReceiveMessage is called when messages have been sent to this character by the engine, level, or objects in levels.

Objects in levels (such as hotspots or other characters) can send this using movement_object.SendMessage("some message string");.

Parameters can be sent by separating them with spaces, and putting quotes around parameters that might contain spaces, then using the TokenIterator object. TODO: Write a TokenIterator example and link to it.

Note: If you're setting up the code to send a message to a character, you might want to choose to only send messages to player characters, or non-player characters. TODO: Example of how to do this filtering.

SetEnabled function

void SetEnabled(bool is_enabled) { }

Required

The SetEnabled hook function is called when movement_object.SetEnabled(bool); gets called on the character by another script.

This function allows the character to enable or disable any objects that should be associated with the character (such as fire ribbons, sounds, shadow objects, character camera item flashes, and water splashes).

This function can also be used to internally set variables so that its functions have no effect. The engine will generally not call the character if it is not enabled (except when it gets re-enabled), but other scripts could theoretically call functions on the character using movement_object_instance.Execute("some code");.

Note: It may be viable to call Dispose() from inside this function, as long as you're careful not to double-delete things.