Starward Rogue:XML - GameEntity Definitions

From Arcen Wiki
Jump to navigation Jump to search

Overview

A lot of these things cross over between any type of entity, and some things are specific to only certain entity types (ships only, or shots only). So we're going to break it out like that, with specific notes for each section as relevant.

An example

<entity name="Goldbug"
display_name="Goldbug"
    	behavior="Stationary" category="Ship"
    	speed="0" turning_resistance="2.25"
    	max_health="50"
    	image_folder="Enemy_Stationary"    
	>
	<system type="Goldbug" offset="0,0" />
	<hitbox radius="32" />
    	<stacked_image src="Attachments/TarraCrawlerArm" size="60" offset="-20,20" />
	<modifier target="MyShots" type="RicochetsOffTerrain"/>
</entity>

Note that the above is all one xml node (entity) with a bunch of attributes, until you get down to the end carat (>). Then the next line has a self-terminating hitbox node (self terminating means it does <hitbox/> rather than having <hitbox></hitbox> -- you self-terminate nodes if they don’t have any sub-nodes). There’s then a stacked_image node, and a modifier node, all of which are sub-nodes of the entity.

Attributes

These are things that go directly on the entity node, in the format attribute=”value”. So in the code <entity name="Goldbug">, name is the attribute and Goldbug is the value for that attribute.

Most Important General

  • name (string)
    • All entities must have a name. The name for an entity must be absolutely unique from any other entities in the game, no matter what file they are in.
    • The name is actually more of a “primary key” identifier, and it’s not something players would ever see.
      • “SDT#$S” is a perfectly valid name if you want, although it’s a super unhelpful one so please don’t do it.
    • By default, the image for any entity is based on its name. This can (and often should be) overridden, however -- the image_name property does that.
      • Ships:
        • By default they will use the image RuntimeData/Images/Ships/yournamehere
      • ItemPickups:
        • By default they will use the image RuntimeData/Images/Items/yournamehere
      • Shots:
        • By default they will use the image RuntimeData/Images/Shots/yournamehere
        • It’s unlikely you will ever define a shot, however. You’ll define bullet patterns and systems and ships, but all a shot is is a graphic and a collision box. You’ll reference those, but not define them.
          • The systems and bullet patterns denote how shots move, what their damage is, speed is, etc, etc, etc.
      • Obstacles:
        • It’s really complicated how these use the images, and tends to be very tileset-dependent, which is not something in the purview of what you’re really designing here. That’s more to do with environment than enemies/items.
  • category (GameEntityCategory)
    • Oi! What is this thing? Depending on what you say, it will be parsed differently.

Ship-Only Stuff

  • display_name (string)
    • THIS is what the player sees as the “name of the ship.”
  • acceleration (float)
    • How fast the ship moves from speed zero to its max speed. Does not impact deceleration speed. Default: insta-accelerates (0).
  • min_ship_speed (int)
    • If the ship is accelerating from zero, what does it immediately jump to? If it's trying to move at all, what's the minimum speed it will go? The default is 20. For ships with low acceleration, setting this higher and then letting them accelerate more slowly to their max speed can be helpful.
  • acceleration_for_momentum_mode (float)
    • like acceleration, but used instead of that number if (and only if) momentum mode is on and this is a player hull.
    • defaults to the normal acceleration value
  • acceleration_for_sprint (float)
    • if this is a player hull and you are accelerating while still under your normal (non-sprinting) top speed, then this value gets added to your acceleration-of-relevance to boost you even faster.
    • once you are at a speed that is higher than your normal max speed -- aka you are in sprinting-only territory -- then this is your sole acceleration value.
    • defaults to the acceleration_for_momentum_mode value
  • deceleration (float)
    • How quickly the ship will stop when it wants to stop. Most useful for player ships. Default: insta-decelerates (0).
  • deceleration_for_momentum_mode (float)
    • like deceleration, but used instead of that number if (and only if) momentum mode is on and this is a player hull
    • defaults to the normal deceleration value
  • deceleration_for_sprint (float)
    • if this is a player hull and you are trying to stop from a speed that is higher than your normal max speed -- aka you are in sprinting-only territory -- then this is your sole deceleration value. Once your speed is back down under your normal max speed, then the deceleration-of-relevance takes over again.
    • defaults to the deceleration_for_momentum_mode value
  • speed (float)
    • What is the maximum speed this ship can move at? This is measured in pixels per second, or thereabouts.
  • sprint_speed (float)
    • the alternate max speed used when sprinting (only matters for the player)
  • max_shield (int)
    • How many shots can the ship completely block per room. Each shield point block one shot of any magnitude and then that block disappears.
  • ship_category (ShipCategory)
    • Determines a lot about how the ship is seeded.
  • time_to_visually_rotate_180_degrees (float, optional, default instant)
    • in seconds, the time it takes to do a complete 180 rotation when changing duration; smaller turns take proportionately less time
    • IMPORTANT NOTE: this is not actually a turning radius. If a ship decides to change direction of _movement_, it does so instantly. Otherwise it's way too easy to get irrevocably stuck on walls since ships have no "reverse gear". What this controls is how long it takes for the ship's rotation to "catch up" with its movement. This is not purely visual, as it impacts the position of the ship's hitboxes and systems (if any of them have offsets other than 0,0).
  • time_to_rotate_firing_direction_180_degrees (float, default instant)
    • if > 0, the entity's effective "angle to reticule" does not update instantly but changes at this finite speed, expressed in the same terms as time_to_visually_rotate_180_degrees
  • knockback_resistance (float)
    • The portion, as 0 to 1, of knockback that entities of this type should ignore. So 0 is no resistance (and default), 0.5 causes the entity to only be knocked back half as far, and >= 1 causes the entity to be immune to knockback. Incidentally, -1 would cause the entity to be knocked back twice as far, though long-distance knockback would be pretty weird since it's an instant location change.
AI-Only Ship Stuff
  • rotates_to_face_firing_direction (bool)
    • Whenever the ship fires, the ship instantly faces the direction of the target Default: false
  • passive_rotation_speed (float)
    • A passive rotation that happens over time, having nothing to do with movement. Default: false
  • behavior (FlockBehaviorType)
    • Determines the way that the ship will act, at least to start off with (something could change that later in the life of the ship). Default: Attacker
  • seeds_directly_in_rooms (bool)
    • Default true. If this is set to false, then the enemy will not naturally show up in the game! This is normally bad, right?
    • The main purpose of this is for multi-stage enemies. With those, when one enemy dies, it turns into another enemy type. If you don’t want that second enemy type showing up except via this process, then set this property to true.
    • TLDR: this is typically true on the later stages of enemies, but omitted everywhere else.
  • damage_per_second_from_touching (float)
    • If a player is touching this ship, how much damage per second does that cause to the player? Default: 10.
  • initial_movement_driving_bullet_pattern (bullet_pattern, optional)
    • the entity spawns with the logic from the first bullet node in this pattern
    • if the pattern has a die node that WILL kill the entity (instantly, no scaling out)
    • if the pattern has no die node and the pattern runs out of actions the ship will switch to normal non-bullet-pattern behavior
  • teleportation_interval (float), teleportation_particle (particle_pattern), teleportation_invisible_time (float), teleportation_telegraph_time (float), teleportation_distance_from_player (int)
    • every (interval) seconds, assuming the entity isn't doing something strange (not doing bullet pattern movement, etc), the entity:
    • spawns (particle) at the origin
    • disappears for (invisible_time) seconds (its systems are also disabled for this time)
    • moves to a point (distance) from the player's current location
    • displays a telegraph animation at the destination for (telegraph_time) seconds
    • spawns (particle) at the destination
    • reappears
  • teleports_at_health_portion_less_than_or_equal_to (float)
    • when the entity is first reduced below X% of max health (expressed from 0 to 1), it teleports using the particle, invisible, telegraph, and distance flags above
  • cloaked_if_has_not_fired_within_last_x_seconds (float), cloaking_fade_time (float)
    • when the entity has not fired within last x seconds, it fades to invisibility over (fade_time) seconds
  • cloaking_goes_partial_over_floor (bool), cloaking_goes_partial_over_window (bool), cloaking_partial_opacity_reduction (float), cloaking_full_to_partial_time (float)
    • when entity is cloaked and over (floor or window), it goes to (1f-partial_opacity_reduction) over (full_to_partial) seconds
  • invincible_when_not_cueing (bool)
    • if this entity is not cueing a weapon at a given instant, it is immune to all damage at that instant
  • spawning_patterns (List<bullet_pattern>)
    • when this entity is spawned by room-generation, instead of just spawning one at the center of the corresponding tile, it picks one of the bullet patterns in this list, then spawns one entity per bullet node that's a direct child of the pattern (so child nodes that do their own spawns don't count)
  • on_death_drops_ability_points (int)
    • the number of ability points this entity drops when it dies
    • the default values are set by the ShipCategory of the enemy. Extra-tough (or weak) enemies in each category should have their values adjusted.
  • movement_delay (float)
    • whenever this entity goes from not moving under power to moving under power, it delays this long (in seconds) before actually starting to move
    • does not affect movement driven by a bullet pattern, familiars driven by orbital logic, etc
  • does_not_prevent_doors_from_opening (bool)
    • this entity doesn't count for whether or not the room is "clear", and thus when the game determines whether or not to seal the room doors
  • frequency_in_enemy_seeding (int, default 100)
    • this is actually an alias to frequency_in_item_pools, which now also impacts normal enemy seeding frequencies, but this name would make more sense in the xml for enemies
    • anyway, setting this to 50 makes the enemy half as likely as others to seed, relatively, and setting it to 200 makes it twice as likely
Familiar-Only Ship Stuff
  • familiar_type (FamiliarType)
    • For familiars that hang out around the player (or behave like normal ships, if set that way), there are different movement patterns that they can use. Default: None.
  • is_familiar_that_dies_when_parent_dies (bool)
    • an entity with this flag without an entity that it is a familiar to instantly dies
  • on_grant_restores_master_entity_shields (bool)
    • when this is initially granted to the player as a familiar, the player's shields are restored to full
  • dies_if_master_entity_leaves_room (bool)
    • when the player changes room, and the player has this as a familiar type, this entity type will not spawn (normally all familiars are re-generated when you change rooms)
  • familiar_regenerates_after_current_room (bool, default true), familiar_regenerates_after_current_floor (bool)
    • if neither of these are set, and this entity is a player familiar, the familiar is gone permanently if it dies
    • if (room) is set, it does the normal behavior of the familiar respawning every room change
    • if (floor) is set, it stops respawning normally until the next floor switch
  • dies_if_master_entity_has_no_shields (bool)
    • if this has no master entity (that is, it's a familiar to that entity) or the master entity's current shield strength is zero, this entity dies
  • on_master_death_transforms_into (entity)
    • only makes sense for stuff used as familiars; when this entity would die because of the master's death (and ONLY for that cause of death), this entity transforms into the specified one like on_death_transforms_into.
Unusual Ship-Only Stuff
  • strafing_time (float)
    • If this is set, then every time the ship fires, it loses the ability to steer for this amount of time, and will just continue on at max speed during that interval. Default 0.
    • This was used a lot in TLF for ships that would do “strafing runs” past the player and then would wheel around in a slow arc and come back. But because the slow-arc style of ship movement is not in SR (because walls), this doesn’t really make as much sense conceptually.
      • Could still be interesting, though, for making an enemy that goes zipping past you while firing, and then has to regroup and turn back around. Would probably only work well with systems on the ship that fire when they have line of sight on the player or else this ship might just always be going nuts.
    • This is really useful if you want to have an invisible, invincible ship that does something like spawn enemies. Though honestly, this is a relic from TLF where stuff spawned offscreen from out of empty space, and here it would probably make more sense to have a visible spawner.
  • never_stops_shots (bool)
    • Funky! Rather than shots hitting this and dying, any shots that hit it stay alive and just keep moving. Default false..
  • always_faces_player (bool)
    • Creepily face the player at all times, regardless of movement. Default false.
  • always_faces_firing_direction (bool)
    • Always face the direction it’s shooting, not moving. Default false.
  • stun_resistance (float, optional)
    • any MovementSpeed modifiers copied to an entity with this, with either a negative Add or a < 1 Multiply, have their duration (if it's > 0) multiplied by 1f - this resistance value.
  • death_spiral_movement_driving_bullet_pattern (bullet_pattern, optional)
    • the entity is incapable of dying normally
    • when the enemy would normally die, it instead switches to this pattern
    • the pattern needs a die node, and that will kill it; otherwise it will never die
  • wake_range (int)
    • used in conjunction with duration=UntilWoken modifiers. When the player ship gets this close AND this shiphas line of sight to the player, then it will wake up.
  • wakes_on_shot (bool)
    • used in conjunction with duration=UntilWoken modifiers. When this ship gets shot, then it will wake up.
  • omit_from_player_hull_selection (bool)
    • only matters for player entities, and causes the game to not include this hull type in the new-run screen where the player selects a hull, and not to consider it when deciding whether the player has or has not fulfilled the "kill final boss with all mechs", etc achievements

Cosmetic Stuff

  • never_renders (bool)
    • The entity is… invisible? Default false.
  • image_name (string)
    • Normally the name of the entity is what is used to find the image for the entity. This lets you override that logic.
    • The two particularly-relevant cases:
      • Ships:
        • When you set this, they will use RuntimeData/Images/Ships/image_name
      • ItemPickups:
        • When you set this, they will use RuntimeData/Images/Items/image_name
  • image_folder (string)
    • This can be used with or without image_name. Often it is used without image_name.
    • For organizational purposes, it’s nice to be able to put graphics into arbitrary subfolders. So for instance, we have an Enemy_Bosses subfolder inside the Ships folder.
    • To make sure that our entity looks for an image in there, we just set image_folder=”Enemy_Bosses” and that’s it.
    • You can do folders-in-folders if you want, too, and then have a forward slash (/) separating those out.
      • Make sure to always use forward slashes when referencing filenames. If you’re working on windows it will work with a backslash, but then someone running this on osx or linux will have it fail.
  • animator_frames (int), animator_sprite_dict_size (int), animator_speed (int)
    • if the filename designated by image_folder and image_name (which may be default values) plus "_Dict" as a suffix exists, image_name will be loaded as an animator with the corresponding (dict_size), (frames), and (speed).
  • description (string)
    • We’re not really using this much for ships. But for ship hulls, we can use this to give players a description of what the pros and cons of that ship hull is. Overall ignore.
    • Also not used on most item pickups, because in their case the system that they are granting should instead have the description.
  • sounds_on_takes_damage (comma-delimited strings list)
    • If this entity takes damage from a shot hitting it, then one of the sounds in the list will be played at random.
    • Default when a GameEntityCategory.Ship: "EnemyHit_DullThud1,EnemyHit_DullThud2"
    • Default when a GameEntityCategory.Obstacle or GameEntityCategory.StationaryObject: "ObstacleHit1,ObstacleHit2,ObstacleHit3,ObstacleHit4,ObstacleHit5"
  • sounds_on_death1 (comma-delimited strings list)
    • If this entity dies, then one of the sounds in the list will be played at random.
  • sounds_on_death2 (comma-delimited strings list)
    • If this entity dies, then one of the sounds in the list will ALSO be played at random. Useful for compound sounds.
  • sounds_on_pickup (comma-delimited strings list)
    • If this entity is an item pickup that is picked up, then it plays a sound from this list at random.
    • Default when using image_folder Modules: "Interaction/ModulePickup1"
    • Default when using image_folder Guns: "Interaction/PickupWeapon1,Interaction/PickupWeapon2,Interaction/PickupWeapon3,Interaction/PickupWeapon4,Interaction/PickupWeapon5,Interaction/PickupWeapon6"
    • Default when a mysterious circuit: "Interaction/PickupMysteriousCircuit"
    • Default when granting a familiar: "ItemCategories/GainFamiliar1,ItemCategories/GainFamiliar2"
    • Default when granting credits: "ItemCategories/CreditsPickup1,ItemCategories/CreditsPickup2"
    • Default when granting some other item: "Interaction/ItemPickup1,Interaction/ItemPickup2,Interaction/ItemPickup3,Interaction/ItemPickup4"
    • Default in any other case: "ItemCategories/PowerupPickup1,ItemCategories/PowerupPickup2"
  • shader (ShaderType)
    • The type of shader that will be used to draw the entity. Generally you’ll want to use Normal, but Additive can be quite interesting also.
  • particle_on_death (particle_pattern, optional)
    • this ship emits this particle when dying
  • particle_on_aoe
    • similar to particle_on_death but scaled according to the size of the aoe (if the entity's death doesn't involve an aoe, the particle is not shown). Replaces the default MissileHit particle in this context.
  • movement_particle (particle_pattern, optional)
    • this ship emits this particle while moving
  • exhaust_offset (int)
    • This is an int that defines how far forward or backward the exhaust is from the ship. Default 0.
  • death_throes_particle
    • when this entity is in death throes, it emits this particle
  • death_throes_fade_to_black_time
    • when this entity is in its death throes, over this period of time it will fade to black
  • draws_below_others (bool)
    • Does this ship draw below all other ships? Aka, the others can pass over it in all cases? There’s basically two layers of ships, anyhow, and this says “go to the bottom one.”
    • This is most useful for shots, saying that these are drawing on the floor under ships rather than above them. Great for ground sludge, etc.
  • draws_as_floor_obstacle (bool)
    • Floor things that sit on the floor, like teleporter pads and pedestals, set this to true so that they will draw really low. Default false.
  • override_mode_color (Color)
    • overrides the normal logic for determining the diffuse applied to the "mode" image of entities of this type. The mode image pulses in intensity, can be used for running lights, etc.
  • is_considered_ground_walker (bool)
    • For ships that are actually walking on the ground (like mechs) rather than hovering, this should be true. Disables their little hover movement.
  • invincible_while_in_same_room_with (List<entity>)
    • while any entity type in this list is present in the room, entities of this type are invincible
  • invincibility_non_sim_special_bullet_patterns (List<bullet_pattern>)
    • while this entity is invincible, for any reason, it emits this non-sim bullet pattern
  • flipX (bool)
    • Should we flip this base image horizontally? This is different from doing a rotation of 180, since this will keep it upright and mirrored rather than being upside-down.
  • flipY (bool)
    • Same as flipX, but in the vertical axis instead.
  • intro_screen_entrance_sounds (List<string>), intro_screen_exit_sounds (List<string>)
    • when the boss intro screen for this entity plays, a random (entrance_sounds) is played when the entity actually enters the screen, and a random (exit_sounds) is played when the entity actually leaves the screen
  • does_not_show_on_stat_counts_and_screens (bool)
    • this entity is not something that is important enough to be counting towards the stats for items, etc. Credit pickups, etc.

Any Type Of GameEntity Stuff

  • max_health (int)
    • How much health does this thing have?
      • For the player, each box in the health bar is 2 health points. So 1 damage is half a block, 2 is a whole block, etc.
    • For enemies, they have VASTLY larger amounts of health, and the player does more damage, too. Average enemy healths might be more like 100 early in the game, whereas an equivalent player health might be 6.
      • The reason for this is based around how players can hit enemies repeatedly and there’s no break between when the player can hit them, versus the players are immune to damage for a short while after taking a hit (during the period of the red flash of showing damage).
  • flies_through_obstacles (bool)
    • Is this thing able to move straight through non-wall obstacles? Default false.
  • wall_collision_reduction (int)
    • If you are looking at the collision data for entities (hit F7), you’ll notice that the red circles are the hitboxes where the entity collides with other entities.
    • But the big blue square box is what it uses in order to collide with walls, because of the nature of rotating multiple circular hitboxes and so on. The blue box is “pessimistic” in order to make sure that it doesn’t look like the enemy is clipping into walls normally, but sometimes it can be too pessimistic.
    • TLDR: if you want to shrink that blue box, then you can use a positive integer here. Default 0.
  • immune_to_all_damage (bool)
    • I’m invincible! ...You’re a looney. Default false
  • on_death_shot_clearing_explosion_radius (int)
    • When I die, how big of a shot-clearing explosion do I make? Default 0.
    • Note that this does not hurt entities except for clearing shots it touches.
  • on_death_transforms_into (string)
    • When this thing dies, does it instead turn into something else? For multi-stage enemies or obstacles, this is how you go from one stage to the next. The string here should be the name of the other entity. Default blank.
  • familiars (comma-delimited strings list)
    • This is the list of entity names in the format “name1,name2” and so on of any familiars the ship might have by default. Most will not have any, but some enemies might have some built in. Who knows!
  • starting_angle (int, optional)
    • all entities of this type will start with this angle when seeded, rather than picking a random angle like normal
  • never_changes_angle_after_spawn (bool, optional)
    • quite aggressively prevents any change to the rotation of all entities of this type; they can still move on whatever angle, but the ship's visual rotation (and thus the offsets of hitboxes and systems on that ship) won't change.
Unusual Stuff For Any Type Of GameEntity
  • gravity_pull_per_second, gravity_falloff_per_unit_distance (both floats, optional)
    • so if you had values of 100 and 0.2, then it would exert a pull on something up to 500 units away, where that pull is 20 at 400 distance, 40 at 300, 60 at 200, 80 at 100, etc
  • gravity_affects_shots, gravity_affects_ships , shot_gravity_affects_friendlies (all bools, optional)
    • you need to set either shots or ships for it to do anything; always affects enemies, but only friendlies if you set that flag
  • gravity_has_repelling_effect (bool, optional)
    • makes the entity's gravity pull do a repulsion instead
  • special_object_type (SpecialObjectType)
    • I’m not even going to really fully define this here, because it’s something that is used on special-casey obstacles and not something that you’d really mess with directly much. Default None.
  • dies_if_origin_system_is_disabled (bool)
    • if this entity was not generated by a system, or if that system's parent entity has died, or if that system is currently disabled for any reason (including toggling), this entity dies
  • transforms_into_item_pickup_on_boss_room_win (ItemPool)
    • when this is in a boss room that has been one, an item from this pool is created at this entity's location and this entity dies
  • cyclical_fading_time_before_fading_out (float), cyclical_fading_time_before_fading_in (float)
    • during the first (out) seconds it gradually fades to transparent; then for the next (in) seconds it gradually fades back to normal but does not collide with anything, then it starts over
  • cannot_be_reduced_below_one_health (bool)
    • doesn't prevent being hit or being damaged, but prevents damage from reducing health to zero so it never actually dies from damage
  • disabled_below_this_percent_health (int)
    • if the entity is below this percent (expressed from 1 to 100) it stops colliding with stuff, stops firing shots (including melee attacks), and is drawn faded out by whatever percent of lost health
  • special_object_type_when_disabled_by_low_health (SpecialObjectType)
    • overrides special_object_type for rendering purposes when below the disabled_below_this_percent_health threshold
  • disappears_if_room_left (bool)
    • when the player leaves the room, this is quietly removed (like shots)
  • do_not_seed_until_achievement_unlocked (AchievementType)
    • the entity is locked until the specified achievement is unlocked
    • only means much for entities and item pickups

ItemPickup-Only Stuff

  • credit_cost (int)
    • required if item_pools contains Shop
  • health_sacrifice_cost (int)
    • similar to credit cost, but "spending" max health instead
  • on_pickup_grants_ship_system (string, optional)
    • when the player picks this up then the game finds the EntitySystem with this name and gives it to the player (swapping out the old system of that type, if any, and putting it on the ground)
  • on_pickup_grants_familiar (string, optional)
    • very similar to on_pickup_grants_ship_system, but instead finds the GameEntity with name==this and grants it as an familiar to the player.
  • on_pickup_grants_item (InventoryItemType)
  • on_pickup_grants_health (bool)
    • When this thing dies, does it give health to the player? Default: false
  • on_pickup_grants_amount (int)
    • Used with several other on_pickup properties. Default 0.
    • With on_pickup_grants_health: this specifies how much health the player gets.
  • auto_pickup (bool)
    • When the player steps on this item, does it automatically go into their inventory? Default false
  • item_pools (list of ItemPool -- required)
  • frequency_in_item_pools (int, default 100)
    • If this is set to more than 100, then it will show up with greater frequency than it otherwise would -- 200 would mean twice as often as it otherwise would.
    • Similarly, 50 means half as often as usual, and 1 is 1% as often as usual.
  • is_harmful_mysterious_circuit (bool), is_helpful_mysterious_circuit (bool)
    • use at most one or the other for a specific entity
    • this pickup is considered a mysterious circuit for the special logic of that concept
    • When a run is started, the game picks (difficulty.NumberOfNegativeMysteriousCircuits) negative circuits at random, and then picks enough positive circuits at random to bring the circuit total up to 10
    • It then randomizes the order
    • It doesn't load a base image; instead it gets an image from Images/Items/MysteriousCircuits
      • This will be consistent within a given run (unless the contents of that image directory are modified between save and load), but different on other runs
    • the first 4 out of the 10 (post randomization) are marked as known
    • unknown ones just show ??? for name and description
    • picking one up marks it known
      • and gives you a popup with its name and description
  • should_be_remembered_as_power_up (bool)
    • the game will remember that the player picked up this power-up, for the remainder of the run
    • this remembering has no direct effects (that was already possible via modifiers with target=OnPickupGrantToPlayer and the like), it's just for record-keeping
  • destroys_system_in_slot (SystemSlotType)
    • when you pick this up it destroys whatever was in the specified slot
  • can_only_be_picked_up_if_you_have_a_system_in_slot (SystemSlotType)
    • if the player does not have a system in this slot, the player cannot pick up this item
  • can_seed_multiple_times_per_floor (bool), can_seed_multiple_times_per_run (bool)
    • normally an item pickup won't be seeded if it's already been seeded this run; the (run) flag makes it do so. The (floor) flag also makes it do that, but also allows it to seed more than once per floor.
  • do_not_seed_if_last_floor (bool)
    • similar to do_not_seed_if_x_floors_down, but only block seeding on the last floor (whether that's 5 or 7 or whatever)
  • do_not_seed_until_unlocked_with_x_ability_points (int)
    • for item pickups, tells the game not to seed it unless it has been unlocked with AP, and specifies the cost
  • can_be_purchased_with_x_ability_points (int)
    • for item pickups, tells the game the player can purchase one of these at the beginning of a run by spending this many AP
    • randomly offers up to three items that have been unlocked or do not need to be unlocked, and that the player has the AP for
  • probation_description (string)
    • when you have a probation modifier from picking up this item, this is the string shown at the top of the screen
    • if this is not set for a probation modifier item, it will use an auto-generated description that may or may not make much sense to the player
  • pick_up_even_if_wastes_resources (bool)
    • if set, this item can be picked up even if it grants health/items that the player can't benefit from because they're at max
    • useful if that health/item grant is just a secondary effect and you want the primary effect to always be available

Seed-Limiting Stuff

For items and monsters and even obstacles, there may be restrictions on when we want them to seed. We may simply not be ready to have the entity as part of the game yet, but we want to be able to test it. Or we may want the player to have gone through whatever prior conditions in order to unlock this monster or item so that it starts appearing.

  • is_not_ready_to_seed (bool)
    • prevents normal seeding of this entity type
    • does not apply to test chamber or tutorials (same goes for the rest of these do_not_seed flags)
  • do_not_seed_unless_have_killed (entity), do_not_seed_unless_have_killed_count (int)
    • prevents normal seeding of this entity type unless the player has killed at least (count) of the specified entity type.
    • This is particularly useful for gating more difficult versions of enemies behind weaker versions. You have to kill a certain amount of the weaker version before the harder one starts getting mixed in, for instance.
  • do_not_seed_if_have_killed (entity), do_not_seed_if_have_killed_count (int)
    • prevents normal seeding of this entity type if the player has killed at least (count) of the specified entity type.
    • This is useful for phasing out easier versions of enemies if they are just trivial past a certain point and you only want to keep the upgraded version.
    • Best advice is to make some overlap at the very least, so you have both the weaker and stronger version for a while before the weaker version gets phased out.
      • Transitions are nice, and a mixture of them can actually be more trouble than just a harder version.
      • So in some cases consider not phasing out weaker versions at all.
  • do_not_seed_in_other_pools_until_picked_up_at_least_once (ItemPool)
    • prevents normal seeding of this entity type except for the specified ItemPool, until it has been picked up once
  • do_not_seed_until_have_won_x_runs (int)
    • prevents normal seeding of this entity type until the player has won (x) runs
    • You can infer if the player has beaten the regular FinalBoss a certain number of times simply by looking at this.
      • For the SuperFinalBoss, look at this and mentally subtract 10 -- the first 10 runs won won't include the SuperFinalBoss.
    • At any rate, this is useful for gating certain things behind the concept of "win a few runs and then things shift more dramatically."
      • More specifically, this is a great way to introduce whole new enemy TYPES, rather than just harder versions of existing enemies.
  • do_not_seed_if_have_won_x_runs (int)
    • prevents normal seeding of this entity type if the player has won (x) or more runs
    • You can use this to phase out a weaker enemy type if you so desire after the player has played a certain amount of time.
      • The general idea is that you can make the density of nasty stuff higher by introducing harder types of enemies and phasing out the weaker types of enemies if you want.
      • Best advice is not to really do this too much, but it is useful sometimes. Even weak enemies have their place.
  • do_not_seed_until_x_floors_down (int)
    • prevents normal seeding of this entity type unless the player is currently on floor (x) or greater of the current run
    • so if you set this to 1 it won't do anything, but if you set it to 2 it will prevent the thing from seeding normally on the first floor of any dungeon
  • do_not_seed_if_x_floors_down (int)
    • prevents normal seeding of this entity type if the player is currently on floor (x) or greater of the current run
    • so if you set this to 1 it won't ever seed, but if you set it to 2 it will prevent the thing from seeding after the first floor of any dungeon
    • This is actually super useful! If you're introducing lots of harder enemies the further down you go into a dungeon, you probably also want to start phasing out weaker ones. Gives more of a sense of increasing danger and unfamiliarity the further down you go.
  • do_not_seed_until_unlocked_with_x_ability_points (AbilityPointCost)
    • prevents any seeding at all until the player has bought this item in the first-floor starting room with some ability points acquired on previous runs.
    • this item will show up randomly in the starting player room on the first floor if the player has at least >= this many ability points when starting the run, and the player can unlock and use the item then.
  • can_be_purchased_with_x_ability_points (AbilityPointCost)
    • this doesn't actually prevent any seeding, but it seems to fit here.
    • this causes the item to show up in the first-floor starting room for purchase at this price if it has already been unlocked.
    • by default, if do_not_seed_until_unlocked_with_x_ability_points has been set, then this will be half of that value.
    • if do_not_seed_until_unlocked_with_x_ability_points is not set, but this variable is, then this item doesn't have to be unlocked but can still be bought at the start of a run.
      • In those circumstances we want to mainly focus on things that the player would find interesting and useful for sure. No need to put this on every item by any stretch.
  • never_seed_more_than_one_per_room (bool)
    • in roomgen, if this is set, and this entity has previously been seeded, it will not be picked by the enemy-picking logic
  • only_seeds_on_special_floor_types (None,Gold)
    • This can be set to None or Gold or even None,Gold. Setting this to None will mean that the entity only seeds in the base game floors, setting it to Gold will mean that the entity only seeds in Gold floors. If this tag is not present then the entity can seed anywhere. Setting this to None,Gold will effectively also mean that the entity will seed anywhere, since there are currently only 2 types of special floor.

Sub-Nodes

Until now, everything has been talking about attributes right on the entity node itself (so <entity attribute=”data”></entity>). Now we’re going to talk about sub-nodes that go inside the entity object, though.

An Example

From this example, there are four sub-nodes: one hitbox, two stacked_images, and one modifier.

<entity name="Goldbug"
display_name="Goldbug"
    	behavior="Stationary" category="Ship"
    	speed="0" turning_resistance="2.25"
    	max_health="50"
    	image_folder="Enemy_Stationary"    
	>
	<system type="Goldbug" offset="0,0" />
	<hitbox radius="32" />
 <stacked_image src="Attachments/TarraCrawlerArm" size="60" offset="-20,20" />    	
<stacked_image src="Attachments/TarraCrawlerArm" size="60" offset="20,20"/>
	<modifier target="MyShots" type="RicochetsOffTerrain"/>
</entity>

A key thing to note about all of these sub-nodes is that they in turn also have attributes of their own. Another key thing to note is that you can have as many copies of these kinds of sub-nodes inside an entity as you want. Aka, you can have 1 hitbox or 10, as needed.

system (default schema)

Overall premise: a system attached to this ship, that can actually do something like shoot or trigger a player ability or whatever. These have locations inside the sim, but cannot collide with anything, have no concept of health, etc.

Example:

<system type="Minigun" offset="20,10" />
Attributes
  • type (EntitySystem name)
    • This tells it what system node to pull from the EntitySystem xml
  • offset (int,int)
    • This is the x,y offset of the center of the system from the center of the entity. Particularly useful when you have many systems on a single enemy.
  • add_degrees_to_shots (float)
    • The shots emitted from this system actually come out with the extra amount of this angle.
  • minimum_difficulty (DifficultyType), maximum_difficulty (DifficultyType)
    • causes this system to not be added to the entity when difficulty is outside the specified range
system (alternate schema for when you want random variation in enemies)

Example:

<system offset="20,10">
	<possibility type="Minigun" chance="3" />
	<possibility type="TestWeapon" chance="1" />
</system>
possibility Attributes
  • chance (int)
    • the relative chance of that system being picked for that spot for each entity of this type being spawned
    • So in this example, it has a 3-out-of-4 chance of picking minigun, and a 1-out-of-4 chance of picking TestWeapon
    • Don't use on player hulls or familiars, or it will reroll every time you enter a new room
  • minimum_difficulty (DifficultyType), maximum_difficulty (DifficultyType)
    • causes this possibility to be ignored outside the specified difficulty range
hitbox

Overall premise: this is a circular space that a ship can be shot in, or that a shot occupies and thus collides with ships via. It’s the part of the ship/shot/whatever that is “solid,” in most respects.

Attributes
  • radius (int)
    • This defines the radius of the red circle of the hitbox (hit F7 to see that).
  • offset (int,int)
    • This is the x,y offset of the center of the hitbox from the center of the entity. Particularly useful when you have many hitboxes on a single enemy.
    • Note that the pessimistic blue bounding box for hitting walls will encompass all the hitboxes you define.
  • incoming_damage_multiplier (float)
    • a shot hitting this particular hitbox will have its damage multiplied by this before it's applied to the entity
    • set to zero for immunity to damage
      • negative numbers act like zero; they don't cause healing
  • on_hit_triggers_systems_of_type (system)
    • a shot hitting this particular hitbox will authorize this system to fire (only matters if it's got firing_timing=Never)
stacked_image

Overall premise: sometimes we want to show multiple images on top of the base image of the ship or shot or what have you. This lets us build up larger ships than we’d otherwise have, and it lets us take off pieces when we move to “later stage” copies of the ship (just remove the stacked_image entries and hitbox entries of relevance when doing that.)

Attributes
  • src (string)
    • This looks in the same folder that the base image of the entity is in, whatever that is. Generally speaking it’s nice to put the attachments in some sort of subfolder, and you can specify that all in this one field (as noted in the example above).
  • size (int)
    • What is the width (and height) of this square image? I’m honestly not positive why we care, but do set it accurately just in case.
  • offset (int,int)
    • This is the x,y offset of the center of the stacked image from the center of the entity. You’re presumably always going to want to set this to something.
  • rotation (int)
    • The number of degrees (0 to 364) that the image should be rotated from the basic orientation of the entity it is stacked on. Default 0.
  • flipX (bool)
    • Should we flip this horizontally? This is different from doing a rotation of 180, since this will keep it upright and mirrored rather than being upside-down.
  • flipY (bool)
    • Same as flipX, but in the vertical axis instead.
  • scale (float)
    • The multiplier that the size of the image is drawn by. Default 1.
  • animator_frames (int), animator_sprite_dict_size (int), animator_speed (int)
    • if (dict_size) > 0, src (plus "_Dict" as a suffix) will be loaded as an animator with the corresponding (dict_size), (frames), and (speed)
  • rotates_to_face_firing_direction (bool)
    • draws the stacked image with angle set to the "firing direction" of the parent entity, which is in turn moderated by firing_direction_rotation_speed if that's set on the entity
  • draws_under_ship (bool)
    • draws the stacked image at a lower draw layer than normal
  • animation_is_proportional_to_movement_under_power (bool)
    • if this is an animated stacked image, its animation speed is multiplied by the parent entity's current powered velocity / its max speed (max 1, min 0)
    • if the parent is not moving under its own power, the animation stops
  • only_shows_if_ship_health_is_above_percent (float)
    • this image only draws if the health of the ship it is on is above the specified percent (in the format 0-1f)
  • only_shows_if_ship_health_is_below_percent (float)
    • this image only draws if the health of the ship it is on is below the specified percent (in the format 0-1f)
  • only_shows_if_ship_has_shields (bool)
    • this image only draws if the ship it is on has any shields at present.
  • only_shows_if_ship_has_no_shields (bool)
    • this image only draws if the ship it is on has no shields at present.
  • only_draw_if_entity_has_one_of_these_systems
    • Takes a list of one or more system names.
Sub-sub node: frame_info
  • frame (int)
    • which frame this applies to, where 0 is the first frame
    • if the animator doesn't have this many frames, this frame_info node has no impact
  • possible_non_sim_patterns (List<bullet_pattern>)
    • when this frame is reached one item is picked from this list and triggered
  • possible_sounds_window (List<string>)
    • when this frame is reached, and the related entity is standing on a window tile, one item is picked from this list and played
  • possible_sounds_non_window (List<string>)
    • when this frame is reached, and the related entity is not standing on a window tile, one item is picked from this list and played
room

Only used for bosses, basically for boss rooms rather than picking a room of that type and then populating it, it picks a boss of the appropriate type (miniboss or boss) and then tries to pick a room that will fit the boss. The rooms that will fit are specified as room sub-nodes on the boss's entity record, like this:

Example:

<room src="Boss/CMP_BossNoWindows" buddies="SmallEnemy,SmallEnemy" />
Attributes
  • src (string, required)
    • the name of the room script to use, relative to RuntimeData/Rooms/ , and without the .txt extension.
    • the room script must contain a B to seed the boss at, or this tag must define boss_spawn_point, or it will throw an error during seeding
  • buddies (List<entity>)
    • the names of other monsters to seed with this specific setup (you can have more than one room node with the same src, and different buddy lists), at the 'U' tiles in the room script
      • if the room script's number of 'U' tiles is not exactly equal to the number of buddies specified here, and buddy_spawn_points is not defined on this tag, it will throw an error during seeding
  • boss_spawn_point (ArcenPoint)
    • specifies where in the room to put the boss, rather than using a B character in the room script to specify that
    • if this is defined AND the room script has a B character, it will throw an error during seeding due to the conflicting spawning rules being specified
  • buddy_spawn_points (List<ArcenPoint>)
    • specifies where in the room to put the buddies, rather than using U characters in the room script to specify that
    • if this is defined AND the room script has a U character, it will throw an error during seeding due to the conflicting spawning rules being specified
    • if more points are specified than buddies, it will cycle back to the start of the buddy list and seed more of them until it's populated all the points
    • if more buddies are specified than points, the extra buddies are ignored
potential_replacement

When the player enters a room for the first time and the game is generating the interior of that room, each time it seeds an entity it checks these entries to see if it should seed something else instead.

If a replacement is selected, that replacement is not then checked for further replacement, to avoid infinite loops.

If more than one replacement is eligible, they are rolled in order, and the first one to pass the roll (if any) is picked and the rest are skipped.

Attributes
  • replace_with (entity, required)
    • the alternate entity to seed
  • minimum_difficulty (DifficultyType), maximum_difficulty (DifficultyType)
    • the difficulty range where this replacement is eligible
  • chance_out_of_100 (int, required)
    • the chance of this replacement actually happening, when eligible
intro_screen_message

No attributes, but the content of the sub-node is used as the message on the boss intro screen for this entity

If more than one is defined, one is picked at random when the intro screen starts

level

For player entities only, this defines the experience-point-based progression when playing as this entity.

Attributes
  • experience_required_after_previous_level (int, required)
    • the number of experience points needed to go from the previous level to this one
  • possible_perks (List<entity>)
    • the set from which the level-up perk selection screen is populated
    • the screen will pick three randomly from this list, or just all of them if the count <= 3
comparison_stat

For player entities only, shown on the new run screen when you mouseover that mech.

Attributes
  • name (string, required)
    • shows on the left of that stat area
  • bars (int)
    • if no value is defined,the right of that stat area shows a bar of 10 cells, with (bars) filled in
  • value (string)
    • if defined, shows on the right area of that stat area
familiar

Basically this is like adding something to the end of the "familiars" list that's already there, but allows you to set some additional info. All these are parsed after the familiars list, which matters for the positioning of SimpleCircleOrbit familiars.

Attributes
  • type (entity)
    • the actual familiar type, same as if you'd put it in the "familiars" list
  • minimum_difficulty (DifficultyType), maximum_difficulty (DifficultyType)
    • the lowest and highest difficulty where this entry will be considered (default: always consider)
  • chance_out_of_100 (int)
    • if this is set, and set to a value less than 100, each time it considers spawning this familiar it only has this % chance of actually doing so
  • radius (int)
    • only meaningful with PtarthianOrbit, sets the radius of the circular orbit
  • starting_angle (float)
    • only meaningful with PtarthianOrbit, sets the starting position on the circular orbit, in terms of degrees around the circle
  • rotation_reversed (bool)
    • only meaningful with PtarthianOrbit (works for SimpleCircleOrbit, but is weird there), if set this makes the familiar rotate in the other direction than it would normally
  • override_degrees_per_second (float)
    • only meaningful with PtarthianOrbit (works for SimpleCircleOrbit, but is weird there), if set this overrides the speed of the orbit (which is normally based on the familiar's speed); you can use negative values instead of the rotation_reversed flag if desired
  • suppress_line_of_sight_check (bool)
    • only meaningful with PtarthianOrbit, SimpleCircleOrbit, and NormalOrbit, if set this causes the familiar to skip the normal "don't fly through walls" logic
    • if you know the familiar will always be in an open space where it doesn't need to check this, or if you want it to fly through walls, this flag helps lower the CPU cost of the familiar because it's not having to do LOS checks every fram