Jump to content

Controls: Difference between revisions

From OpenBOR
Line 327: Line 327:


To access to the buffer, you will need to first acquire the buffer pointer from player properties, then select an index. This will in turn provide the pointer to buffer entry where you may access the desired property.  
To access to the buffer, you will need to first acquire the buffer pointer from player properties, then select an index. This will in turn provide the pointer to buffer entry where you may access the desired property.  
[[File:Openbor command buffer hierarchy.png|left|thumb]]
=== Get buffer entry ===
=== Get buffer entry ===
Once you have the buffer pointer, supply an index from <code>0</code> - <code>255</code> to get the buffer entry pointer. Note, as this is a ring buffer, indexes correspond to array elements, not the chronological order of recorded events. <syntaxhighlight lang="c" line="1">
Once you have the buffer pointer, supply an index from <code>0</code> - <code>255</code> to get the buffer entry pointer. Note, as this is a ring buffer, indexes correspond to array elements, not the chronological order of recorded events. <syntaxhighlight lang="c" line="1">

Revision as of 14:12, 19 July 2026

Introduction

OpenBOR’s native control scheme is designed to be intuitive as possible and conform to established standards for side scrolling action games. Through various native options and scripting techniques (see below), creators can map these controls to perform any desired action. You can even rename or remove commands entirely to create a unique control scheme specific to your game. In turn, players may map available commands to desired keys or buttons via Control Options in the main menu.

Default Keys

OpenBOR allots each player the following inputs and default mappings.

Left

  • Move player left on screen.
  • Cycle to previous column or option on current row in menus.
  • Cycle to previous character during player select.

Right

  • Move player right on screen.
  • Cycle to next column or option on current row in menus.
  • Cycle to next character during player select.

Up

  • Moves player up on screen (along Z axis) in pseudo 3D stages.
  • Cycle upward through rows in menus.
  • Cycle to previous color palette during player select.

Down

  • Moves player down on screen (along Z axis) in pseudo 3D stages.
  • Crouch/Duck in 2D stages.
  • Cycle downward through rows in menus.
  • Cycle to next color palette during player select.

Special

  • Player Special.
  • Blocking if enabled (see ajspecial below).

Jump

  • Player Jumping.

Attack 1

  • Basic attack.
  • Confirm a selection.
  • Pick up items.

Attack 2 - Unused.

Attack 3 - Unused.

Attack 4 - Unused.

Start

  • Begin or continue a game.
  • Join multiplayer game in progress (if not an active player).
  • Pause/resume game in progress (if an active player).

Screenshot

  • Send a screen capture to Screenshots folder.
  • Access main menu during game pause.

In addition, there is a universal Escape key:

  • Cycle to previous page in menu.
  • Exit player select and return to main menu.
  • Shut down engine when not in game or in a menu.
  • Resume a paused game.

Renaming Keys

You can rename keys by adding a file to the data folder named menu.txt. Use the following commands in the menu.txt file to customize or remove keys.

  • renamekey {target command} {new name}
  • disablekey {target command}

Accepted keys for rename or removal:

  • moveup
  • movedown
  • moveright
  • moveleft
  • attack
  • attack2
  • attack3
  • attack4
  • jump
  • special
  • start
  • screenshot

In this example, menu.txt renames Special to Defend and Attack 2 to Magic, while removing unused commands Attack 3 and Attack 4. This presents the player with a more polished set of controls that match actual in game functionality.

renamekey	attack	Attack
renamekey	attack2 Magic
renamekey	special	Defend

disablekey	attack3
disablekey	attack4
Renaming keys to to match their function in your projects adds a professional touch.

Alternate Command Functions

You can add to or change the existing functionality of commands entirely. Common examples include adding additional attacks mapped to the normally unused Attack 2+ commands, remapping the player special, or special move sequences.

Command

com {sequence} {freespecial#}

Enables mapping single button or command sequences to trigger a desired Freespecial animation. Place com into a model’s text header. You may add multiple commands to a model with no practical limits other than hardware resources.

  • Sequence is the input or sequence of inputs to trigger the command. Each sequence may contain up to sixty-four steps from the following list. Default in game leeway for command sequences is 50 elapsed time ticks, or 0.25 seconds allowed between each new step:
    • f - Left/right (same direction character is facing).
    • b - Left/Right (opposite direction character is facing).
    • l - Left.
    • r - Right.
    • u - Up.
    • d - Down.
    • a - Attack 1.
    • a2 - Attack 2.
    • a3 - Attack 3.
    • a4 - Attack 4.
    • j - Jump.
    • s - Special.
    • st - Start.
    • sc - Screenshot.
    • + - Chord input. Ex. a + s means press Attack and Special together. Each chord counts as one sequence step, regardless of how many inputs it may encompass.
      • +[int grace time (default 0)] - Optional grace period to press all buttons in a chord. Ex. a + s +[100] allows up to 100 ticks to press a and s. Longer grace periods make commands easier, but may also result in overly delayed activation if players are slow with their fingers and give the game an unresponsive, laggy feeling. Adjust to suit your game needs.
    • ~ - Release key. Ex. ~a means release Attack.
    • [min time (default 0)][max time (default 0)] - Time constraints for holding key. Must be used in conjunction with another command. Max time is optional.
      • a[50] - Hold a for at least 50 ticks.
      • a[50][200] - Hold a for at least 50 ticks, but no more than 200 ticks.
      • a[0][200] - Hold a with no minimum hold time, but no more than 200 ticks.
      • a[0] - Hold a without any time constraints.
    • *[int time] - Auto trigger if held to time. Ex. a*[50] means action triggers automatically after holding a for 50 ticks.
    • :[int time] - Time allowed between sequence steps. Ex. d -> f -> a -> :[100] means you have 100 ticks to enter d, then 100 ticks to enter f, and another 100 to enter a. If not used, the global default applies instead. You may place this anywhere in a sequence other than as part of a chord. Does not count as a sequence step.
    • -> - Delimiter. Optional, but highly recommended for readability. Does not count as a sequence step.
  • Freespecial# is the animation that plays in response to the command, assuming other conditions are met (i.e. having enough energy if the animation has an energy cost).

Example Sequences

Command Result
com a2 freespecial1 Press Attack 2.
com d -> f -> a freespecial1 Press Down, then Forward, then Attack.
com a + j freespecial1 Press Attack and Jump on the same tick.
com a + j +[100] freespecial1 Press Attack and Jump within 100 ticks of each other.
com ~a freespecial1 Release Attack.
com f[0] + a freespecial1 Hold Forward and press Attack.
com f[50] + a freespecial1 Hold Forward for at least 50 ticks, then press Attack while still holding Forward.
com a[0] + j freespecial1 Hold Attack for any duration and press Jump.
com a[0][200] + j freespecial1 Press Jump while Attack has been held for no more than 200 ticks.
com a[50][200] + j freespecial1 Press Jump while Attack has been held for 50 through 200 ticks.
com a[50] + ~a freespecial1 Hold Attack for at least 50 ticks, then release it.
com a[50][200] + ~a freespecial1 Release Attack after holding it for 50 through 200 ticks.
com a*[50] freespecial1 Trigger automatically when Attack has been held for 50 ticks.
com f -> ~f freespecial1 Press Forward, then release Forward.
com b[60][180] + ~b -> f + a freespecial1 Hold Back for 60 through 180 ticks, release it, then press Forward and Attack together.
com b[60][180] + ~b -> f + a +[20] freespecial1 Perform the same charge command with a 20-tick grace period for the Forward and Attack chord.
Tips

Don’t let the name “freespecial” confuse you. Freesepcials are not inherently “special”. You can use them to create any sort of animation you like, from a simple punch or kick, evasive movement, taunt, or whatever else.

Overly exact commands in a side scrolling environment are not necessary and may make your game more spastic to control instead of more precise. For example, to create the classic Hadouken input you should use d -> f -> a rather than d -> d + f -> f -> a.

If two or more sequences overlap, the more complex input from the player “wins”. This is the same command scheme seen in most fighting games. Again using the Haouken example, you could create a command for the normal Hadouken d -> f -> a, and another for Shinku Hadouken as d -> f -> d -> f -> a. OpenBOR will recognize if a player uses the Shinku Hadouken input even though the last portion of it is identical to the Hadouken input.

Cancel

cancel {int start frame} {int end frame} {int hits} {sequence} {freespecial#}

Cancel is similar to Command, in that it maps command sequences to activate a Freespeical. However, cancels are unique to a single animation and can interrupt the animation on a successful input. This allows “canceling” from one animation to another for customized chains and combos.

Place cancels into the animation header of any Jump, Attack, Freespecial, or Follow animation. You may add as many cancels as you like to an animation to give it multiple cancel options. The animation you cancel into may also have its own cancels to create an entire series of combo branch points.

  • Start Frame - The first frame in animation when cancel is available - Remember that frames are 0 indexed.
  • End Frame - The last frame in animation when cancel is available.
  • Hits - Required number of combo hits, if any, before cancel is available.
  • Sequence - Identical to command sequence parameter.
  • Freespeciall# - Identical to command Freespecial# parameter.

Cancel is otherwise identical to Command and follows the same rules (input sequence, energy cost, etc.).

Maximum Freespecials

maxfreespecials {int}

#default
maxfreespecials 8

Models.txt command that sets the highest numbered freespecial animation available globally. The default and minimum value is 8. For example, maxfreespecials 12 enables animation identifiers freespecial1 through freespecial12.

Tip: You may allocate any number, but don't set some silly amount like 100 when you only need a few more freespecial animations. This wastes memory. Figure out what you actually need and allocate accordingly.

Attack and Jump Special

ajspecial {flags}

#default
ajspecial special

Controls mapping of the native breakaway special attack and enables blocking for players. Accepts one of the following:

  • special (default) – Special attack mapped to Special key.
  • double – Special attacks mapped to Attack + Jump. Blocking enabled (if entity has a Block animation) and mapped to Special key.
  • attack2 – Special attack mapped to Attack 2 key.
  • attack3 – Special attack mapped to Attack 3 key.
  • attack4 – Special attack mapped to Attack 4 key.

Ajspecial is exposed to script as a global_config property. Use the following constants:

  • openborconstant("AJSPECIAL_KEY_SPECIAL")
  • openborconstant("AJSPECIAL_KEY_DOUBLE")
  • openborconstant("AJSPECIAL_KEY_ATTACK2")
  • openborconstant("AJSPECIAL_KEY_ATTACK3")
  • openborconstant("AJSPECIAL_KEY_ATTACK4")
void config = openborvariant("global_config");

int value = get_global_config(config, "ajspecial");

value = openborconstant("AJSPECIAL_KEY_ATTACK3");
set_global_config(config, "ajspecial", value);

Legacy

Prior to OpenBOR 4.0, ajspecial accepted only the following:

  • 0 (default) – Special attack mapped to Special key.
  • 1 – Special attacks mapped to Attack + Jump. Blocking enabled (if entity has a Block animation) and mapped to Special key.

Key Control Constants

Key Constants

Each key press sent from the control reading layer maps to one of the following constants. These are usually not used by creators unless you are capturing and manipulating the detected keys with InputAll (see below).

  • openborconstant("SDID_ATTACK") - Attack key.
  • openborconstant("SDID_ATTACK2") - Attack 2 key.
  • openborconstant("SDID_ATTACK3") - Attack 3 key.
  • openborconstant("SDID_ATTACK4") - Attack 4 key.
  • openborconstant("SDID_ESC") - Escape key.
  • openborconstant("SDID_JUMP") - Jump key.
  • openborconstant("SDID_MOVEDOWN") - Move down key.
  • openborconstant("SDID_MOVELEFT") - Move left key.
  • openborconstant("SDID_MOVERIGHT") - Move right key.
  • openborconstant("SDID_MOVEUP") - Move up key.
  • openborconstant("SDID_SCREENSHOT") - Screenshot key.
  • openborconstant("SDID_SPECIAL") - Special key.
  • openborconstant("SDID_START") - Start key.

Event Constants

Once OpenBOR has detected a physical key press, it is mapped and stored using the following event constants. These are then used to execute the appropriate in game action. You may in turn use these constants to evaluate and even manipulate detected events (see below). Note these constants are actually bit masks.

  • openborconstant("FLAG_ATTACK") - Attack key.
  • openborconstant("FLAG_ATTACK2") - Attack 2 key.
  • openborconstant("FLAG_ATTACK3") - Attack 3 key.
  • openborconstant("FLAG_ATTACK4") - Attack 4 key.
  • openborconstant("FLAG_ANYBUTTON") - Shortcut for any action key (jump, special, any attack).
  • openborconstant("FLAG_BACKWARD") - Left or right key in opposite direction player's controlled entity faces.
  • openborconstant("FLAG_CONTROLKEYS") - Shortcut for any game control (anything other than Start, Screenshot, or Escape).
  • openborconstant("FLAG_ESC") - Escape key.
  • openborconstant("FLAG_FORWARD") - Left or right key in same direction player's controlled entity faces.
  • openborconstant("FLAG_JUMP") - Jump key.
  • openborconstant("FLAG_MOVEDOWN") - Down key.
  • openborconstant("FLAG_MOVELEFT") - Left key.
  • openborconstant("FLAG_MOVERIGHT") - Right key.
  • openborconstant("FLAG_MOVEUP") - Up key.
  • openborconstant("FLAG_SCREENSHOT") - Screenshot key.
  • openborconstant("FLAG_SPECIAL") - Special key.
  • openborconstant("FLAG_START") - Start key.

Key Events

When native options aren’t enough, OpenBOR provides several layers of key events. These allow you to capture an incoming key or command, and insert your own scripts to perform any action you can imagine.

Key Status

OpenBOR maintains three key status values for each player. Each value is an integer you can apply bitwise logic to obtain an individual on/off flag for each key.

  • Hold – An engine update begins. If a ey is on, the key is held.
  • Press – Player presses a key
  1. InputAll – Runs on initial player input before before any engine key handling or key scripts take place. Use this event if you want to change the command input itself. Examples include reversing player controls or creating shortcut keys for muti-button presses.
  2. Level keyscript#
  3. Entity keyscript
  4. Global key#.c
  5. Global keyall.c
  6. Default key action.

Command Buffer

In order to detect key sequences and execute actions, OpenBOR keeps a 256 slot ring buffer for each player. Each buffer in houses the last last 256 player input events (press or release of any key). Individual events host the following data:

  • Newly pressed keys.
  • Snapshot of inputs held when a positive edge occurs.
  • Newly released keys.
  • Previously held keys.
  • Currently held keys.
  • Elapsed Time of event.
  • Tick time of event.

In turn the key properties are bit-fields that may contain one more key event masks.

To access to the buffer, you will need to first acquire the buffer pointer from player properties, then select an index. This will in turn provide the pointer to buffer entry where you may access the desired property.

Get buffer entry

Once you have the buffer pointer, supply an index from 0 - 255 to get the buffer entry pointer. Note, as this is a ring buffer, indexes correspond to array elements, not the chronological order of recorded events.

void command_buffer = get_player_property(player, openborconstant("PLAYER_PROPERTY_COMMAND_BUFFER"));
int index = 2; // 0 - 255

void buffer_entry = get_command_input_event_object(command_buffer, index);

Access Buffer entry property

Once you have obtained a buffer entry pointer, you may get or set buffer properties as follows.

Held

Keys previously held and released at event.

// Get
int key_held = get_command_input_event_property(buffer_entry, openborconstant("COMMAND_INPUT_EVENT_PROPERTY_HELD"));

// Set
int new_key_held = openborconstant("FLAG_MOVEUP") | openborconstant("FLAG_JUMP");

set_command_input_event_property(buffer_entry, openborconstant("COMMAND_INPUT_EVENT_PROPERTY_HELD"), new_key_held);

Hold

Keys previously held at event and still active.

// Get
int key_hold = get_command_input_event_property(buffer_entry, openborconstant("COMMAND_INPUT_EVENT_PROPERTY_HOLD"));

// Set
int new_key_hold = openborconstant("FLAG_MOVEUP") | openborconstant("FLAG_JUMP");

set_command_input_event_property(buffer_entry, openborconstant("COMMAND_INPUT_EVENT_PROPERTY_HOLD"), new_key_hold);

Press

Keys newly pressed at event.

// Get
int key_press = get_command_input_event_property(buffer_entry, openborconstant("COMMAND_INPUT_EVENT_PROPERTY_PRESS"));

// Set
int new_key_press = openborconstant("FLAG_MOVEUP") | openborconstant("FLAG_JUMP");

set_command_input_event_property(buffer_entry, openborconstant("COMMAND_INPUT_EVENT_PROPERTY_PRESS"), new_key_press);

Press Chord

Snapshot of inputs when a positive edge (key press) occurs. For example, if player is holding f and presses a, the event's press chord will contain the key event masks openborconstant("FLAG_FORWARD") and openborcosntant("FLAG_ATTACK").

// Get
int key_press_chord = get_command_input_event_property(buffer_entry, openborconstant("COMMAND_INPUT_EVENT_PROPERTY_PRESS_CHORD"));

// Set
int new_key_press_chord = openborconstant("FLAG_MOVEUP") | openborconstant("FLAG_JUMP");

set_command_input_event_property(buffer_entry, openborconstant("COMMAND_INPUT_EVENT_PROPERTY_PRESS_CHORD"), new_key_press_chord);

Release

Key release on event.

// Get
int key_release = get_command_input_event_property(buffer_entry, openborconstant("COMMAND_INPUT_EVENT_PROPERTY_RELEASE"));

// Set
int new_key_release = openborconstant("FLAG_MOVEUP") | openborconstant("FLAG_JUMP");

set_command_input_event_property(buffer_entry, openborconstant("COMMAND_INPUT_EVENT_PROPERTY_RELEASE"), new_key_release);

Ticks

SDL ticks when event occurred. This property is not used by native logic.

// Get
int key_ticks = get_command_input_event_property(buffer_entry, openborconstant("COMMAND_INPUT_EVENT_PROPERTY_TICKS"));

// Set
int new_key_ticks = openborconstant("FLAG_MOVEUP") | openborconstant("FLAG_JUMP");

set_command_input_event_property(buffer_entry, openborconstant("COMMAND_INPUT_EVENT_PROPERTY_TICKS"), new_key_ticks);

Time

Elapsed time when event occurred. This property is important for sequence timing, so be careful of causing logic errors if you choose to modify it.

// Get
int key_time = get_command_input_event_property(buffer_entry, openborconstant("COMMAND_INPUT_EVENT_PROPERTY_TIME"));

// Set
int new_key_time = openborconstant("FLAG_MOVEUP") | openborconstant("FLAG_JUMP");

set_command_input_event_property(buffer_entry, openborconstant("COMMAND_INPUT_EVENT_PROPERTY_TIME"), new_key_time);