Jump to content

Factions

From OpenBOR

Factions control how entities interact with each other. Faction settings independently determine which groups an entity belongs to, which entities it considers hostile, and which entities it can damage.

Faction properties are divided into two systems, each comprised of supporting properties:

  • Group factions act on a set of 52 predefined faction group flags.
    • Membership - Which factions the entity belongs to.
    • Hostility - Which factions the AI and targeting functions consider enemies.
    • Damage - Which factions the entity can damage with attacks.
    • Indirect Damage - Which factions the entity can damage collaterally (ex. thrown by another entity).
  • Type factions use legacy entity types such as player, enemy, and npc.
    • Hostility - Which types the AI ad targeting functions consider enemies.
    • Damage - Which types the the entity can damage with attacks.
    • Indirect Damage - Which types the entity can damage collaterally (ex. thrown by another entity).

Both systems may be combined for highly specific interaction rules.

Note this means that by design, hostility and damage permissions are separate. An entity may consider another entity hostile without being able to damage it, or damage an entity it does not consider hostile.

Factions properties are script accessible and stored in both entity and model structures - meaning faction data exists at three levels - template, starting default, and current condition. This in turn enables on the fly swapping and restoration. For example, an entity might be affected with mind control by modifying its group flags for a limited time, then restored from its starting defaults when the effect wears off.

Design

OpenBOR's faction API is intentionally designed to function either as a standalone faction system or as a hardware-speed primitive for complex higher-level relationships. Its 52 native factions are obviously more than most projects will ever approach. Even so, creators may instead arrange them as behavioral flags for any number of custom factions. Creator-defined factions may use several flags, share flags with other factions, change their assignments during play, or use no native group at all.

Groups may represent geography, allegiance, species, political status, temporary conditions, or any other distinction useful to the project. The resulting combinations are expressed through four hardware-native masks: membership, hostility, direct damage, and indirect damage.

Each property supports 252 possible group arrangements, not merely a choice among 52 factions. Together, the four faction properties provide approximately 4.11 * 1062 possible configurations. That's roughly 4 * 1038 configurations for every star in the observable universe, using NASA's upper estimate.

Higher-Level Example

Suppose a project tracks Game of Thrones (or A Song of Ice and Fire if you prefer) houses in its own relationship table. For a Stark-aligned entity, the table might resolve the current political state as follows:

Creator Faction Current State Resolved Membership OpenBOR Membership
House Stark Northern loyalist The North, Westeros, Living A B G
House Greyjoy Allied with the North The North, Westeros, Living A B G
House Greyjoy In rebellion Unaligned with the North B G
House Lannister Southern loyalist Westeros, Living B C G
House Lannister Sworn to the Night's Watch The North, Westeros, Night's Watch, Living A B D G
Wildlings Beyond the Wall Free Folk, Beyond the Wall, Living E F G
Wildlings Settled in the North The North, Westeros, Free Folk, Living A B E G
Night King Leading the Army of the Dead Beyond the Wall, Army of the Dead F H

For a Stark entity hostile toward the Southern Kingdoms and Army of the Dead:

faction_group_member A B G 
faction_group_hostile C H 
faction_group_damage_direct C H 
faction_group_damage_indirect C H

The Lannister southern loyalist matches group C, while the Lannister sworn to the Night's Watch does not. Both remain House Lannister in the creator's system, but their current OpenBOR faction behavior differs.

Group factions

Group factions use named flags. OpenBOR provides 52 normal faction groups:

a  b  c  d  e  f  g  h  i  j  k  l  m
n  o  p  q  r  s  t  u  v  w  x  y  z

a1 b1 c1 d1 e1 f1 g1 h1 i1 j1 k1 l1 m1
n1 o1 p1 q1 r1 s1 t1 u1 v1 w1 x1 y1 z1

Faction names are case-insensitive. For example, a1 and A1 identify the same faction.

An entity may belong to multiple factions and may target multiple factions with each property. For example, an entity belonging to factions a and b1 matches any faction check containing either group. There is no additional CPU or memory cost for number of faction groups in use by an object.

Group values

Value Description
a through z Selects one of the first 26 normal faction groups.
a1 through z1 Selects one of the second 26 normal faction groups.
all0 Selects every normal faction group from a through z.
all1 Selects every normal faction group from a1 through z1.
all Selects every normal faction group.
neutral Inert faction flag. Hostility and damage checks ignore it.
no_copy Prevents the affected faction property from copying to child entities, projectiles, weapons, alternate models, and similar derived entities.
player_verses Ignores applicable level and versus settings when checking hostility or damage between players.
type_inclusive Includes the corresponding type-faction property. Both the group check and type check must pass.
type_exclusive Uses only the corresponding type-faction property, overriding the normal group check.

The player_verses, type_inclusive, and type_exclusive flags have no useful effect in faction_group_member. They control how hostile and damage properties perform their checks.

Member

Defines the faction groups to which the entity belongs.

faction_group_member {factions}

# Default
faction_group_member a

Multiple groups may be supplied:

faction_group_member a c g1 z1

An entity using this example is a member of factions a, c, g1, and z1.

Damage direct

Defines the faction groups the entity can damage with normal attacks.

faction_group_damage_direct {factions}

# Default
faction_group_damage_direct a type_inclusive
Value Effect
a through z Permits direct damage against members of the selected original faction groups.
a1 through z1 Permits direct damage against members of the selected additional faction groups.
all0 Permits direct damage against members of every normal group from A-Z.
all1 Permits direct damage against members of every normal group from A1-Z1.
all Permits direct damage against members of every normal faction group.
neutral No effect on damage checks.
no_copy Prevents this property from copying to derived entities or alternate models.
player_verses Ignores applicable level and versus restrictions when damaging other players.
type_inclusive Requires both this group check and faction_type_damage_direct to pass.
type_exclusive Ignores this group list and uses only faction_type_damage_direct.

Damage indirect

Defines the faction groups the entity can damage when thrown, blasted, or otherwise used as an indirect attack.

For example, when a player throws an enemy into a group of other enemies, the thrown enemy's indirect damage properties determine which entities it can hit.

faction_group_damage_indirect {factions}

# Default
faction_group_damage_indirect a type_inclusive
Value Effect
a through z Permits indirect damage against members of the selected first 26 faction groups.
a1 through z1 Permits indirect damage against members of the selected second 26 faction groups.
all0 Permits indirect damage against members of every normal group from A-Z.
all1 Permits indirect damage against members of every normal group from A1-Z1.
all Permits indirect damage against members of every normal faction group.
neutral No effect on damage checks.
no_copy Prevents this property from copying to derived entities or alternate models.
player_verses Ignores applicable level and versus restrictions when damaging other players.
type_inclusive Requires both this group check and faction_type_damage_indirect to pass.
type_exclusive Ignores this group list and uses only faction_type_damage_indirect.

Hostile

faction_group_hostile {factions}

# Default
faction_group_hostile a type_inclusive

Defines the faction groups the entity considers hostile. Hostility generally controls targeting and AI behavior rather than damage permission.

Value Effect
a through z Marks members of the selected first 26 faction groups as hostile.
a1 through z1 Marks members of the selected second 26 faction groups as hostile.
all0 Marks members of every normal faction group from A-Z as hostile.
all1 Marks members of every normal faction group from A1-Z1 as hostile.
all Marks members of every normal faction group as hostile.
neutral No effect on hostility checks.
no_copy Prevents this property from copying to derived entities or alternate models.
player_verses Ignores applicable level and versus restrictions when checking hostility toward other players.
type_inclusive Requires both this group check and faction_type_hostile to pass.
type_exclusive Ignores this group list and uses only faction_type_hostile.

Type factions

Prior to OpenBOR 4.0, faction behavior relied solely on entity types. Type-faction properties preserve that behavior and may be used alone or combined with group factions.

Type properties target entity types rather than named faction groups. Common type values include:

end_level
enemy
item
none
npc
obstacle
panel
player
projectile
shot
steamer
text_box
trap

Default type-faction values depend on the model's own type. For example, enemy models are normally hostile toward players, while players can normally damage enemies and obstacles.

Group properties must contain type_inclusive or type_exclusive before their corresponding type property affects the interaction check.

Damage direct

faction_type_damage_direct <types>

Default: Varies by model type. See Type.

Defines the entity types this entity can damage with normal attacks.

faction_type_damage_direct enemy npc obstacle

Accepted values include:

  • Any valid entity type.
  • no_copy - Prevents this property from copying to derived entities or alternate models.

Damage indirect

faction_type_damage_indirect <types>

Default: Varies by model type. See Type.

Defines the entity types this entity can damage when thrown, blasted, or otherwise used as an indirect attack.

faction_type_damage_indirect enemy obstacle

Accepted values include:

  • Any valid entity type.
  • no_copy - Prevents this property from copying to derived entities or alternate models.

Hostile

faction_type_hostile <types>

Default: Varies by model type. See Type.

Defines the entity types this entity considers hostile.

faction_type_hostile player npc

Accepted values include:

  • Any valid entity type.
  • no_copy - Prevents this property from copying to derived entities or alternate models.

Interaction modes

The group-property control flags determine how group and type factions work together.

Group-property configuration Result
No type flag Uses only the group-faction check.
type_inclusive Requires both the group-faction check and corresponding type-faction check to pass.
type_exclusive Uses only the corresponding type-faction check.

For example:

faction_group_damage_direct c d e type_inclusive
faction_type_damage_direct enemy npc

This entity can directly damage another entity only when both conditions are true:

  1. The target belongs to faction c, d, or e.
  2. The target is an enemy or npc type.

Removing type_inclusive would make the type list irrelevant to the direct-damage check. Replacing it with type_exclusive would make the group list irrelevant.

Setup

Model text

Use the faction commands in a model header to establish its starting faction configuration.

The following model belongs to factions a, b, and a1. It is hostile toward and can directly damage members of factions c, d, e, and z1. When thrown or blasted, it can damage members of its own factions.

faction_group_damage_direct   c d e z1
faction_group_damage_indirect a b a1
faction_group_hostile         c d e z1
faction_group_member          a b a1

The next example adds type_inclusive. Targets must now pass both the group and type checks.

faction_group_damage_direct   c d e z1 type_inclusive
faction_group_damage_indirect a b a1 type_inclusive
faction_group_hostile         c d e z1 type_inclusive
faction_group_member          a b a1

faction_type_damage_direct    enemy npc
faction_type_damage_indirect  player
faction_type_hostile          enemy npc

Level spawn

Faction commands may appear in level spawn entries to override a model's faction settings for that spawn. Syntax is identical to model text.

The following spawn belongs to factions e and e1, while treating factions g, h, and h1 as hostile and directly damageable.

spawn                           Shermie
coords                          350 160
faction_group_damage_direct     g h h1
faction_group_damage_indirect   e e1
faction_group_hostile           g h h1
faction_group_member            e e1
at                              10

Script access

Faction properties are accessible through get_faction_property() and set_faction_property().

Faction group values are unsigned 64-bit masks. Use the named constants supplied by openborconstant() and combine them with bitwise operators. Raw numeric faction values are implementation details and should not be used.

Get property

Returns a faction property from a faction handle.

mixed value = get_faction_property(
    void faction_handle,
    int property
);

Set property

Sets a faction property on a faction handle.

set_faction_property(
    void faction_handle,
    int property,
    mixed value
);

Property constants

Property constant Value type Description
FACTION_PROPERTY_GROUP_DAMAGE_DIRECT Unsigned 64-bit integer Group direct-damage mask.
FACTION_PROPERTY_GROUP_DAMAGE_INDIRECT Unsigned 64-bit integer Group indirect-damage mask.
FACTION_PROPERTY_GROUP_HOSTILE Unsigned 64-bit integer Group hostility mask.
FACTION_PROPERTY_GROUP_MEMBER Unsigned 64-bit integer Group membership mask.
FACTION_PROPERTY_TYPE_DAMAGE_DIRECT Unsigned 64-bit integer Type direct-damage mask. Combine applicable TYPE_* constants.
FACTION_PROPERTY_TYPE_DAMAGE_INDIRECT Unsigned 64-bit integer Type indirect-damage mask. Combine applicable TYPE_* constants.
FACTION_PROPERTY_TYPE_HOSTILE Unsigned 64-bit integer Type hostility mask. Combine applicable TYPE_* constants.

Group constants

The following constants are available through openborconstant():

Constant Description
FACTION_GROUP_NONE Empty faction mask.
FACTION_GROUP_NEUTRAL Inert faction flag.
FACTION_GROUP_NO_COPY Prevents the property from copying.
FACTION_GROUP_PLAYER_VERSES Ignores applicable player-versus-player restrictions.
FACTION_GROUP_TYPE_EXCLUSIVE Uses only the corresponding type-faction check.
FACTION_GROUP_TYPE_INCLUSIVE Requires both group and type checks.
FACTION_GROUP_ALL_NORMAL_0 Contains every normal faction from A-Z.
FACTION_GROUP_ALL_NORMAL_1 Contains every normal faction from A1-Z1.
FACTION_GROUP_ALL_NORMAL Contains every normal faction from A-Z and A1-Z1.
FACTION_GROUP_ALL Contains every normal faction plus the operational player-versus and type-check flags.
FACTION_GROUP_DEFAULT Default group configuration.
FACTION_GROUP_NO_CHECK Combined mask of flags excluded from normal faction-group matching.
FACTION_GROUP_A through FACTION_GROUP_Z Original 26 normal faction flags.
FACTION_GROUP_A1 through FACTION_GROUP_Z1 Additional 26 normal faction flags.

Script example

The following example combines factions a, c, a1, and z1, then assigns the result as the faction membership mask.

mixed faction_mask =
      openborconstant("FACTION_GROUP_A")
    | openborconstant("FACTION_GROUP_C")
    | openborconstant("FACTION_GROUP_A1")
    | openborconstant("FACTION_GROUP_Z1");

set_faction_property(
    faction_handle,
    openborconstant("FACTION_PROPERTY_GROUP_MEMBER"),
    faction_mask
);

Individual flags may be tested with bitwise AND:

mixed faction_mask = get_faction_property(
    faction_handle,
    openborconstant("FACTION_PROPERTY_GROUP_MEMBER")
);

if (faction_mask & openborconstant("FACTION_GROUP_A1"))
{
    // Entity is a member of faction A1.
}