Starward Rogue:XML - BulletPattern Definitions
Contents
Overview
Unlike the other things being read in (GameEntity, EntitySystem, etc) this is more of a programming language than a chunk of data. That is, it's imperative as much as it is descriptive.
It's also different in that it relies heavily on template "variables" to avoid duplicating a lot of xml when you want your bullets to do the same or similar thing a lot.
Here's an example usage:
In a file in Configuration/EntitySystem/
<system name="TestWeapon" category="Weapon" shot_type="BulletPointedYellow" damage_type="Ballistic" attack_power="40" fire_rate="3" range_actual="500" shots_per_salvo="1" targeting_logic="Lead" firing_timing="AllTheTime" special_bullet_patterns="TestVariablePattern" > </system>
And in a file in Configuration/BulletPattern/
<bullet_pattern name="TestVariablePattern"> $TestOuterVar SPEED=500 </bullet_pattern>
And in a file in Configuration/BulletPatternVariables/
<var name="TestOuterVar"> <bullet angle="0" speed="0"> <loop iterations="5"> <spawn> <bullet_pattern> $TestInnerVar A1=45 A2=-45 $TestInnerVar A1=-45 A2=45 </bullet_pattern> </spawn> <wait time="0.1" /> </loop> <die /> </bullet> </var> <var name="TestInnerVar"> <bullet angle="[A1]" speed="[SPEED]" shot_type="BulletBentEnergyRed"> <wait time="0.5" /> <loop iterations="5"> <change angle="[A2]" time="0.0001" /> <wait time="1" /> <change angle="[A1]" time="0.0001" /> <wait time="1" /> </loop> <die /> </bullet> </var>
Now let's trace what's going on:
Massively more confusing than the examples of the data-chunk files, right? Well, welcome to the neighborhood, it's at least as bad as it sounds.
- There's a system (TestWeapon) with special_bullet_patterns="TestVariablePattern" . This tells it to ignore a lot of its normal firing logic and instead "do whatever that pattern tells you" whenever it fires.
- In TestVariablePattern's definition, it references the template "TestOuterVar" and passes in the value "500" for the parameter "SPEED"
- the 500 could have been hardcoded later on, but this allows multiple patterns to use this behavior with different bullet velocities without extra copy-and-paste
- In TestOuterVar's definition, it:
- creates a single "starter" bullet with no velocity (it's still visible in this example, but that's probably not desirable), which does the following five times:
- spawn another bullet pattern, whose description references the template "TestInnerVar" twice, each time with different parameter values for A1 and A2
- Again, the angles could have been hardcoded, but then the code in TestInnerVar would have to be written twice: once for each different set of values. Code duplication is the enemy!
- waits a tenth of a second (before going back to the beginning of the loop)
- spawn another bullet pattern, whose description references the template "TestInnerVar" twice, each time with different parameter values for A1 and A2
- and then the starter bullet dies
- creates a single "starter" bullet with no velocity (it's still visible in this example, but that's probably not desirable), which does the following five times:
- In TestInnerVar's definition, it:
- creates a BulletBentEnergyRed bullet with angle = A1 and speed = SPEED (note: TestOuterVar doesn't have to pass SPEED in, that's already defined), which then:
- travels at that course and speed for half a second
- changes angle to A2 (instantly, that's the time="0.0001"
- travels at that course and speed for a full second
- changes back to A1
- travels at that course and speed for a full second
- switches back and forth four more times
- dies
- creates a BulletBentEnergyRed bullet with angle = A1 and speed = SPEED (note: TestOuterVar doesn't have to pass SPEED in, that's already defined), which then:
The overall result is that it fires two strings of bullets 45 degrees on either side of the line to the target, and the lines "crisscross" five times before disappearing.
IMPORTANT NOTE: the use of the template variables was completely optional and has no bearing on the actual xml schema: those just cause a bunch of text replacement when the xml is being read in. Here's how the file in Configuration/BulletPattern/ would have looked without any template vars, and also the actual xml text that the game would have _seen_ in the main parsing in the TestVariablePattern case:
<bullet_pattern name="TestVariablePattern"> <bullet angle="0" speed="0"> <loop iterations="5"> <spawn> <bullet_pattern> <bullet angle="45" speed="500" shot_type="BulletBentEnergyRed"> <wait time="0.5" /> <loop iterations="5"> <change angle="-45" time="0.0001" /> <wait time="1" /> <change angle="45" time="0.0001" /> <wait time="1" /> </loop> <die /> </bullet> <bullet angle="-45" speed="500" shot_type="BulletBentEnergyRed"> <wait time="0.5" /> <loop iterations="5"> <change angle="45" time="0.0001" /> <wait time="1" /> <change angle="-45" time="0.0001" /> <wait time="1" /> </loop> <die /> </bullet> </bullet_pattern> </spawn> <wait time="0.1" /> </loop> <die /> </bullet> </bullet_pattern>
Node Types and Attributes
var
this is a template variable that can be referenced elsewhere to save effort. Can only be defined in files in Configuration/BulletPatternVariables/
attributes
- name (string)
- must be unique, this is how it's referenced
sub-node(s)
- any
- the game just basically copy-and-pastes it into whatever referenced it, after replacing any parameter names (enclosed in square brackets) with their values in that context
IMPORTANT NOTE
the syntax for referencing a variable is to have:
- a $ as the first non-whitespace on a line
- followed immediately by the name of the variable
- followed by zero or more:
- one space
- followed immediately by a parameter name (alpha-numeric, starts with a letter)
- followed immediately by an equal sign
- followed immediately by the parameter value (alpha-numeric, most symbols probably work, no spaces)
bullet_pattern
this is the main thing being defined, but the node can also be used within a bullet's spawn sub-node
attributes
- name (string, required unless this is within a spawn node)
- Must be unique across all BulletPattern's being loaded by the game
sub-node(s)
- bullet
bullet
this is the main point of defining anything, and corresponds to a single GameEntity spawned by the firing system
attributes
- angle (float)
- the angle of the shot's initial course, relative to the angle to the target (or to the angle of the shot spawning this, if this is a nested pattern)
- speed (float)
- the speed of the shot's initial course
- dumbfire (bool, optional)
- the shot's initial angle is relative to the firing ship's rotation, rather than to the angle to the target
- shot_type (GameEntity name, optional)
- if set, makes this shot this entity type, rather than whatever the system normally produces
- damage_type (DamageType, optional)
- if set, makes this shot do this kind of damage, rather than what the system specifies
- interval_mult (int, optional)
- if > 1, makes the pattern only do this bullet every N times. So if it this is 3 then this bullet entry will be processed on the system's 1st firing, 4th firing, 7th firing, etc
- requires_difficulty_at_least (DifficultyType, optional)
- If the difficulty is set lower than the specified one, this bullet does not fire.
- requires_difficulty_at_most (DifficultyType, optional)
- If the difficulty is set higher than the specified one, this bullet does not fire.
- damage_mult (float, optional)
- multiplies the damage done by the shot
- behavior (FlockBehaviorType, optional)
- makes the shot use the designated behavior, for example PathfindingAttacker, instead of normal shot movement logic; note that this will tend to override any angle/etc setting in the pattern
- requires_parent_scale_less_than_or_equal_to (float, optional)
- if the firing entity's scale (visual size multiplier) is currently greater than this, the bullet entry is not processed
- requires_parent_scale_greater_than_or_equal_to (float, optional)
- if the firing entity's scale is currently less than this, the bullet entry is not processed
- requires_parent_health_percent_less_than_or_equal_to (float, optional)
- if the firing entity's health percent (expressed between 0 and 100) is greater than this, the bullet entry is not processed
- requires_parent_health_percent_greater_than_or_equal_to (float, optional)
- if the firing entity's health percent (expressed between 0 and 100) is less than this, the bullet entry is not processed
- requires_firing_system_range_to_player_less_than_or_equal_to (float, optional)
- if the firing entity's range to the player is greater than this, the bullet entry is not processed
- requires_firing_system_range_to_player_greater_than_or_equal_to (float, optional)
- if the firing entity's range to the player is less than this, the bullet entry is not processed
- is_invisible_and_does_not_collide_with_ships (bool, optional)
- the shot does not render and does not collide with ships; still moves normally and can collide with terrain
- this would be useful for a bullet that should spawn something when it either expires from duration or hits a wall
- the shot does not render and does not collide with ships; still moves normally and can collide with terrain
- on_death_ability_explosion_radius (int, optional)
- if > 0, the size of the shot-clearing pulse when this shot is removed from the game
- spawns_offset_from_firing_entity (ArcenPoint, optional)
- sets the origin of the shot relative to the center of the firing entity. Primarily useful for quasi-melee weapons
- moves_with_firing_entity (bool, optional)
- the shot maintains its position relative to the firing entity, as that entity moves around. Primarily useful for quasi-melee weapons
- rotates_around_firing_entity (bool, optional)
- the shot rotates with the firing entity, combined with the previous flag it updates its position to also stay constant relative to the firing ship's central axis (so if it's to the upper right, and you do a 180, it will be to the lower left on the screen)
sub-node(s)
- modifier, wait, die, change, spawn, spawn_entity, loop
Note: all of these except "modifier" are "actions", and happen in sequence after the shot's creation. "modifier" nodes can be anywhere in the list, but their order is not significant (for consistency please put them all at the top of the node body, above all actions)
wait
this just makes the shot continue whatever it was doing for a certain period of time before proceeding to the next action
attributes
- time (float)
- seconds until the bullet moves to the next step
die
this makes the shot go away
attributes
- instant (bool, optional)
- if set, the bullet disappears instantly when it hits this step. Otherwise it starts the scaling-down process that removes it shortly thereafter.
change
this changes some aspect of the shot's movement, often doing that over a period of time.
attributes
- time (float)
- how long the change takes, in seconds. During the change the shot will interpolate (lerp) between its state at the beginning of the change and the target state
- a value of 0.001 can be used for "instant" changes
- how long the change takes, in seconds. During the change the shot will interpolate (lerp) between its state at the beginning of the change and the target state
- time_rand (two floats separated by a comma, optional)
- the random component for the time attribute
- so if you have <change time="4" time_rand="-1,1" /> then the change will actually take some time between 3 and 5 seconds
- the random component for the time attribute
- relative (bool or special string, optional)
- if this is "ToPlayer" then the angle is set relative to the angle to the player at the beginning of this change
- this doesn't make sense combined with changes other than angle
- if this is "true" then all changes in this action are considered relative to the shot's state at the beginning of the action. That way you can speed up by 10 by just putting speed="10" rather than computing it in your head while writing the pattern
- if this is "ToPlayer" then the angle is set relative to the angle to the player at the beginning of this change
- speed (float, optional)
- the target speed (or delta, if relative is true)
- speed_rand (two floats separated by a comma, optional)
- the random component for the speed attribute
- angle (float, optional)
- the target angle (or delta, if relative is true)
- angle_rand (two floats separated by a comma, optional)
- the random component for the angle attribute
- offset_from_parent (ArcenPoint, optional)
- the target offset from the parent (or delta, if relative is true), useful with on bullet entries with the moves_with_firing_entity flag when making quasi-melee weapons swing around
- parent_scale (float)
- the target scale of the parent entity (or delta, if relative is true); 1 is normal, 0 is basically-not-there
- "parent" means the ship with the system that fired this bullet (or the shot that spawned this bullet)
- the target scale of the parent entity (or delta, if relative is true); 1 is normal, 0 is basically-not-there
- self_scale (float)
- the target scale of this bullet (or delta, if relative is true); 1 is normal, 0 is basically-not-there
- this impacts both visual size and the size of the hitbox
- the target scale of this bullet (or delta, if relative is true); 1 is normal, 0 is basically-not-there
spawn
this creates more shots
sub-node(s)
- must contain exactly one bullet_pattern node
spawn_entity
this creates something... anything really, though the game will get really weird if you spawn another player-type hull or something like that
attributes
- name (GameEntity type name)
- the name of the entity type to spawn
- spawn_at_parent (bool, optional)
- the entity will spawn at the firing ship's location; otherwise it spawns at the shot's current location
loop
this makes the shot do the same thing multiple times in a row, without you having to repeat yourself. Code duplication is the enemy!
attributes
- iterations (int)
- the number of times to perform the body
sub-node(s)
- wait, die, change, spawn, spawn_entity, loop