Modding:Active Parts
This page is about modding. See the modding overview for an abstract on modding. |
Many object parts (the entity components specified in ObjectBlueprints.xml
with part
tags) are based on IActivePart
architecture. IActivePart
provides standardized support functionality for parts that actively, conditionally enact behavior. Much of this support can be powerfully reconfigured at the object blueprint level without requiring scripting intervention, though whether a given variation in behavior will actually work depends on the quality of the final part's integration with IActivePart
.
The most crucial concepts to how active parts work are subjects, the objects the part operates on, and status, the part's operational state. The basic form of a part's integration with its IActivePart
support is for it to check its status, and if it is operational, to apply its behavior to its subjects.
Statuses
These are the possible active part statuses. Only the status Operational represents working functionality; other statuses are failure modes.
Active Part Statuses |
---|
Operational |
NeedsSubject |
EMP |
Broken |
Rusted |
Booting |
NotHanging |
SwitchedOff |
RealityStabilized |
LimbIncompatible |
PrimarySystemOffline |
Unpowered |
LocallyDefinedFailure |
Status-determining configuration points
The following configuration points control the determination of an active part's status. Defaults indicated are at the IActivePart
level. Many individual parts set different defaults.
Individual parts can also override the method public virtual bool GetActivePartLocallyDefinedFailure()
to implement their own failure modes; if this method returns true, the part's status will be LocallyDefinedFailure. Generally public virtual string GetActivePartLocallyDefinedFailureDescription()
should also be overridden; its return value will be used instead of "LocallyDefinedFailure" for status display (see below).
Name | Type | Behavior if enabled | Status on failure | Default |
---|---|---|---|---|
ChargeUse | int | An amount of charge the part consumes in order to operate. Fails if the parent object does not have at least this much charge available. The implementing part chooses when to actively consume charge; when it does, this amount will be consumed unless overridden. | Unpowered | 0 |
ChargeMinimum | int | An amount of charge the part requires to be available in order to operate. Fails if the parent object does not have at least this much charge available. | Unpowered | 0 |
IsBootSensitive | bool | Looks for a sibling BootSequence part; fails if it finds one and its boot sequence is currently in progress.
|
Booting | false |
IsBreakageSensitive | bool | Fails if the parent object is Broken. | Broken | true |
IsEMPSensitive | bool | Fails if the parent object is ElectromagneticPulsed. | EMP | false |
IsHangingSensitive | bool | Looks for a sibling Hangable part; fails if it finds one and it is not hanging.
|
NotHanging | false |
IsPowerSwitchSensitive | bool | Looks for a sibling PowerSwitch part; fails if it finds one and it is switched off.
|
SwitchedOff | false |
IsRealityDistortionBased | bool | Fails if normality in the parent object's immediate context prevents reality distortion. | RealityStabilized | false |
IsRustSensitive | bool | Fails if the parent object is Rusted. | Rusted | true |
NeedsOtherActivePartOperational | string | Looks for a sibling active part of the name specified. Fails if it does not find one, or that part's status is not Operational. | PrimarySystemOffline | null |
NeedsOtherActivePartEngaged | string | Looks for a sibling active part of the name specified. Fails if it does not find one, or that part returns false from the method IsActivePartEngaged() . (By default, this is the same as a check for Operational status, but particular parts can override the method to implement different behavior, for whatever usage of "engaged" is desired.)
|
PrimarySystemOffline | null |
RequiresBodyPartCategory | string | Fails if the parent object is not equipped on a body part of the category specified. The body part categories are "Animal", "Arthropod", "Plant", "Fungal", "Protoplasmic", "Cybernetic", "Mechanical", "Metal", "Wooden", "Stone", "Glass", "Leather", "Bone", "Chitin", "Plastic", "Cloth", "Psionic", and "Extradimensional". | LimbIncompatible | null |
Subject-determining configuration points
The following boolean configuration points control identification of an active part's subject or subjects. All default to false at the IActivePart
level. Many individual parts set different defaults.
Individual parts can also override the method public virtual bool WorksFor(GameObject obj)
in order to implement additional restrictions on what objects are valid subjects.
If an active part has no valid subjects, its status will be NeedsSubject.
Where the parent object's current cell is referred to, it only applies to a cell the parent object is directly present in (not a cell its carrier or equipper is in, for example).
Name | Behavior if enabled |
---|---|
MustBeUnderstood | Filters the results from other settings. The player is only a valid subject if they understand the parent object (i.e. they have it identified). Other objects are only valid subjects if they have an Intelligence stat. |
WorksOnAdjacentCellContents | Objects in cells adjacent to the parent object's current cell are valid subjects. |
WorksOnCarrier | The parent object's InInventory (i.e. an object containing the parent object as inventory) is a valid subject. An object that has the parent object equipped as a thrown weapon is also a valid subject.
|
WorksOnCellContents | Objects in the parent object's current cell, other than the parent object, are valid subjects. |
WorksOnEnclosed | An object the parent object has Enclosed via the Enclosing part is a valid subject.
|
WorksOnEquipper | An object that has the parent object "equipped properly" is a valid subject. For an armor or shield, this means being worn on the appropriate body part. For an implant, this means being implanted. Otherwise, this means being equipped other than as a thrown weapon. |
WorksOnHolder | An object that has the parent object equipped in a hand or missile weapon is a valid subject. |
WorksOnImplantee | An object that has the parent object implanted is a valid subject. |
WorksOnInventory | Objects in the parent object's inventory, if any, are valid subjects. |
WorksOnSelf | The parent object is a valid subject. |
WorksOnWearer | An object that has the parent object equipped on the appropriate body part for its armor or shield configuration is a valid subject. |
Status display configuration points
These fields control the display of the active part's status in the parent object's short description.
Name | Type | Behavior if set | Default |
---|---|---|---|
DescribeStatusForProperty | string | Status will be described if the player has a positive value for this int property. This field is not normally used directly; usually it is set via IsTechScannable. | null |
IsBioScannable | bool | Sets DescribeStatusForProperty to the property corresponding to bioscanning ("BioScannerEquipped") and StatusStyle to "bio". | false |
IsStructureScannable | bool | Sets DescribeStatusForProperty to the property corresponding to structural scanning ("StructureScannerEquipped") and StatusStyle to "structure". | false |
IsTechScannable | bool | Sets DescribeStatusForProperty to the property corresponding to techscanning ("TechScannerEquipped") and StatusStyle to "tech". | false |
NameForStatus | string | Replaces the default name shown for the part in status display. The default is the name of the part class (so this is particularly crucial for very generic parts like IntPropertyChanger ).
|
null |
StatusStyle | string | The display style to use for status if it is displayed. The possible styles are "angry", "bio", "leet", "ooc", "plain", "structure", and "tech". This field is not normally used directly; usually it is set via IsTechScannable. | "plain" |
Other configuration points
Name | Type | Behavior if set | Default |
---|---|---|---|
ReadyColorString | string | When the part determines its status to be Operational, it sets the ColorString field on the parent object's Render part to this value.
|
null |
ReadyDetailColor | string | When the part determines its status to be Operational, it sets the DetailColor field on the parent object's Render part to this value.
|
null |
DisabledColorString | string | When the part determines its status to be other than Operational, it sets the ColorString field on the parent object's Render part to this value.
|
null |
DisabledDetailColor | string | When the part determines its status to be other than Operational, it sets the DetailColor field on the parent object's Render part to this value.
|
null |
IPoweredPart
Many active parts inherit IPoweredPart
, which is a variant of IActivePart
intended for more technical applications. It sets the following defaults:
Field | Default |
---|---|
ChargeUse | 1 |
IsBootSensitive | true |
IsEMPSensitive | true |
IsPowerSwitchSensitive | true |
IsTechScannable | true |
Scripting integration
Introduction
The most common way for parts to use IActivePart
support is the IsReady()
method. The example part below heals its parent object every turn:
using System;
namespace XRL.World.Parts
{
[Serializable]
public class HealSelfEveryTurn : IActivePart
{
public HealSelfEveryTurn
{
WorksOnSelf = true;
}
public override bool WantEvent(int ID, int cascade)
{
return
base.WantEvent(ID, cascade)
|| ID == EndTurnEvent.ID
;
}
public override bool HandleEvent(EndTurnEvent E)
{
if (IsReady(UseCharge: true))
{
ParentObject.Heal(1);
}
return true;
}
}
}
In this very basic integration, the UseCharge: true
parameter makes the part consume the amount of charge in its ChargeUse
field. Without further configuration, this will have defaulted to 0. We want to call for charge to be used as the appropriate time to enable specific configurations that do use charge to be created.
The part above is directly acting on its parent object, without checking any information about subjects provided by IActivePart
. It sets WorksOnSelf
because without any identifiable subject, the part will not be operational. Instances could set other subject-determining configuration points, which could change when the part works (since it won't work without a subject), but won't change what object the healing is applied to. A more flexible integration could look like:
using System;
namespace XRL.World.Parts
{
[Serializable]
public class HealEveryTurn : IActivePart
{
public override bool WantEvent(int ID, int cascade)
{
return
base.WantEvent(ID, cascade)
|| ID == EndTurnEvent.ID
;
}
public override bool HandleEvent(EndTurnEvent E)
{
if (IsReady(UseCharge: true))
{
foreach (GameObject obj in GetActivePartSubjects())
{
obj.Heal(1);
}
}
return true;
}
}
}
This part will need subject-determining configuration before it can operate. It will apply its healing to whatever subject or subjects are identified by that configuration, though, making it more powerful for designers to work with.
Methods
IsReady()
public bool IsReady(
bool UseCharge = false,
bool IgnoreCharge = false,
bool IgnoreBootSequence = false,
bool IgnoreBreakage = false,
bool IgnoreRust = false,
bool IgnoreEMP = false,
bool IgnoreRealityStabilization = false,
bool IgnoreSubject = false,
bool IgnoreLocallyDefinedFailure = false,
int MultipleCharge = 1,
int? ChargeUse = null,
bool UseChargeIfUnpowered = false
)
Parameter | Behavior |
---|---|
UseCharge | If true, an attempt will be made to consume any charge required by the part if it is not otherwise disabled. If false, charge will be checked for but not consumed. |
IgnoreCharge | If true, all considerations related to charge will be ignored. |
IgnoreBootSequence | If true, IsBootSensitive will be ignored.
|
IgnoreBreakage | If true, IsBreakageSensitive will be ignored.
|
IgnoreRust | If true, IsRustSensitive will be ignored.
|
IgnoreEMP | If true, IsEMPSensitive will be ignored.
|
IgnoreRealityStabilization | If true, IsRealityDistortionBased will be ignored.
|
IgnoreSubject | If true, the part will not check for subjects or consider itself to be in SubjectNeeded status. |
IgnoreLocallyDefinedFailure | If true, GetActivePartLocallyDefinedFailure() will not be checked and the part will not consider itself to be in LocallyDefinedFailure status.
|
MultipleCharge | Multiplies charge use in a fashion intended to represent operation over multiple turns. This means that ChargeMinimum is not multiplied, and that if charge is being consumed and not enough charge is available for the full set of multiples of charge use, charge will be consumed for however many multiples can be accommodated.
|
ChargeUse | Overrides the part's ChargeUse setting for purposes of this call. The default of null means to use the part's ChargeUse setting.
|
UseChargeIfUnpowered | With UseCharge: false , this parameter triggers consuming charge in the event that the part is otherwise operational but does not have enough charge. This is to allow certain process flows to ensure that they'll consume the last charge available in their parent object instead of leaving a tiny bit unused (with liquid-fueled energy cells, leaving a bit behind means leaving 1 dram of liquid, which creates confusing UX).
|
Returns true if the part's status is Operational. Triggers any appropriate render color changes. Updates the last known status.
IsDisabled()
public bool IsReady(
bool UseCharge = false,
bool IgnoreCharge = false,
bool IgnoreBootSequence = false,
bool IgnoreBreakage = false,
bool IgnoreRust = false,
bool IgnoreEMP = false,
bool IgnoreRealityStabilization = false,
bool IgnoreSubject = false,
bool IgnoreLocallyDefinedFailure = false,
int MultipleCharge = 1,
int? ChargeUse = null,
bool UseChargeIfUnpowered = false
)
The same as IsReady()
, but with the opposite return value.
GetActivePartStatus()
public ActivePartStatus GetActivePartStatus(
bool UseCharge = false,
bool IgnoreCharge = false,
bool IgnoreBootSequence = false,
bool IgnoreBreakage = false,
bool IgnoreRust = false,
bool IgnoreEMP = false,
bool IgnoreRealityStabilization = false,
bool IgnoreSubject = false,
bool IgnoreLocallyDefinedFailure = false,
int MultipleCharge = 1,
int? ChargeUse = null,
bool UseChargeIfUnpowered = false
)
Identical to IsReady()
, but returns an ActivePartStatus
that can be used to determine what failure mode the part is in. Useful for constructing more helpful failure messaging.
Triggers any appropriate render color changes. Updates the last known status.
WasReady()
public bool WasReady()
Returns whether the active part's status was Operational the last time it was evaluated.
WasDisabled()
public bool WasDisabled()
Returns whether the active part's status was other than Operational the last time it was evaluated.
GetLastActivePartStatus()
public ActivePartStatus GetLastActivePartStatus()
Returns what the active part's status was the last time it was evaluated.
ConsumeCharge()
public bool ConsumeCharge(int? ChargeUse = null)
Triggers consumption of charge from the parent object, irrespective of operational status. The ChargeUse
parameter can be used to override the part's ChargeUse
setting; the default of null means do not override.
public bool ConsumeCharge(int MultipleCharge, int? ChargeUse = null)
As above, but MultipleCharge
has the same behavior as for IsReady()
.
ConsumeChargeIfOperational()
public bool ConsumeChargeIfOperational(
bool IgnoreBootSequence = false,
bool IgnoreBreakage = false,
bool IgnoreRust = false,
bool IgnoreEMP = false,
bool IgnoreRealityStabilization = false,
bool IgnoreSubject = false,
bool IgnoreLocallyDefinedFailure = false,
int MultipleCharge = 1,
int? ChargeUse = null,
bool UseChargeIfUnpowered = false,
bool NeedStatusUpdate = false
)
Triggers consumption of charge from the parent object if the part is operational. Parameters that are also present in IsReady()
behave the same as there.
Normally, status will only be obtained (and therefore color changes triggered and last known status updated) if there is any charge consumption to be performed. NeedStatusUpdate
can be set to true to indicate that status should always be obtained.
GetActivePartSubjects()
public virtual List<GameObject> GetActivePartSubjects()
Returns a list of the active part's current subjects.
GetActivePartFirstSubject()
public virtual GameObject GetActivePartFirstSubject()
Returns the active part's first subject, or null if it has none.
public virtual GameObject GetActivePartFirstSubject(Predicate<GameObject> Filter)
Returns the first of the active part's subject that matches the filter specified, or null if none do.
IsObjectActivePartSubject()
public virtual bool IsObjectActivePartSubject(GameObject obj)
Returns whether the object specified is one of the active part's subjects.
ActivePartHasMultipleSubjects
public virtual bool ActivePartHasMultipleSubjects()
Returns whether the active part has at least two subjects.
GetActivePartSubjectCount()
public virtual int GetActivePartSubjectCount()
Returns how many subjects the active part has.
ForeachActivePartSubjectWhile()
public virtual bool ForeachActivePartSubjectWhile(Predicate <GameObject> pProc, bool MayMoveAddOrDestroy = false)
Calls pProc
on each of the active part's subjects, terminating if pProc
returns false. MayMoveAddOrDestroy
should be set to true if the code in pProc
might result in any object being moved, created, or destroyed.
AnyActivePartSubjectWantsEvent()
public virtual bool AnyActivePartSubjectWantsEvent(int ID, int cascade)
For integration with the MinEvent
system. Returns true if any of the active part's subjects want to receive the event designated.
ActivePartSubjectsHandleEvent()
public virtual bool ActivePartSubjectsHandleEvent(MinEvent E)
For integration with the MinEvent
system. Sends the event to each of the active part's subject, terminating early and returning false if any of the event handlers return false.
GetActivePartLocallyDefinedFailure()
public virtual bool GetActivePartLocallyDefinedFailure()
Intended to be overridden by inheritors to define their own failure conditions. Returns false by default; a locally defined failure will be considered to exist if it returns true.
GetActivePartLocallyDefinedFailureDescription()
public virtual string GetActivePartLocallyDefinedFailureString()
Intended to be overridden by inheritors to define the description to provide in status display for their own failure conditions. Returns null by default; a non-null return value will be used in place of "LocallyDefinedFailure" when displaying a LocallyDefinedFailure status.
IsActivePartEngaged()
public virtual bool IsActivePartEngaged()
Intended to be overridden by inheritors to define whether the part is "engaged" for purposes of NeedsOtherActivePartEngaged
. Returns IsReady()
by default.
GetStatusSummary()
public virtual string GetStatusSummary(ActivePartStatus Status)
For some specified status values, returns a string summary intended for player visibility. Some statuses return null, generally because it makes less sense to make them visible to the player. The status summaries are:
Status | Summary |
---|---|
EMP | "{{W|EMP}}" |
Unpowered | "{{K|unpowered}}" |
SwitchedOff | "{{K|switched off}}" |
Booting (only if BootSequence.IsObvious is true)
|
"{{b|warming up}}" |
any other status besides Operational and NeedsSubject | "{{r|nonfunctional}}" |
Can be overridden by inheritors to provide a status summary for LocallyDefinedFailure status or otherwise alter the default behavior.
public string GetStatusSummary()
Returns GetStatusSummary(GetActivePartStatus())
.
AddStatusSummary()
public void AddStatusSummary(StringBuilder SB)
Retrieves GetStatusSummary()
and, if it is not null or empty, appends " ("
, the summary, and ")"
to the StringBuilder passed. This is to support code like this:
public override bool HandleEvent(GetShortDescriptionEvent E)
{
E.Postfix.AppendRules(AppendRulesDescription, AddStatusSummary);
return true;
}
GetOperationalScopeDescription()
public string GetOperationalScopeDescription()
Returns a readable general description of what objects the part operates on, based on its subject-determining configuration points. Example return values might be its user
or itself and its vicinity
.
List of active parts
Active Parts |
---|
AccelerativeTeleporter |
ActiveLightSource |
AddsMutationOnEquip |
AddsRep |
AloePorta |
AmbientCollector |
AnimateObject |
ArtificialIntelligence |
Banner |
Bed |
BioAmmoLoader |
BleedingOnHit |
BootSequence |
BroadcastPowerReceiver |
BroadcastPowerTransmitter |
BurnMe |
Campfire |
CannotBeInfluenced |
Capacitor |
CatacombsExitTeleporter |
Chair |
ChargeSink |
Circuitry |
Clockwork |
ComputeNode |
CrossFlameOnStep |
Cursed |
Cybernetics2BiodynamicPowerPlant |
Cybernetics2MedassistModule |
Cybernetics2MicromanipulatorArray |
Cybernetics2OnboardRecoilerImprinting |
Cybernetics2OnboardRecoilerTeleporter |
Cybernetics2PenetratingRadar |
CyberneticsTerminal2 |
DamageContents |
DecoyHologramEmitter |
DeploymentMaintainer |
DestroyMe |
DischargeOnHit |
DischargeOnStep |
Displacement |
Displacer |
ElectricalPowerTransmission |
EmergencyTeleporter |
Enclosing |
EnergyAmmoLoader |
EnergyCell |
EnergyCellSocket |
Engulfing |
EquipCharge |
EquipIntProperties |
EquipStatBoost |
FabricateFromSelf |
Fan |
FlareCompensation |
Flywheel |
FoliageCamouflage |
FollowersGetTeleport |
FoodProcessor |
ForceEmitter |
ForceProjector |
FugueOnStep |
FusionReactor |
Gaslight |
GenericPowerTransmission |
GenericTerminal |
GlimmerAlteration |
GrandfatherHorn |
GritGateMainframeTerminal |
HologramMaterialPrimary |
HUD |
HydraulicPowerTransmission |
HydroTurbine |
InductionCharger |
InductionChargeReceiver |
IntegralRecharger |
IntegratedPowerSystems |
IntPropertyChanger |
ItemElements |
LatchesOn |
LeaksFluid |
LifeSaver |
LiquidFueledEnergyCell |
LiquidFueledPowerPlant |
LiquidProducer |
LiquidPump |
LiquidRepellent |
LowStatBooster |
MagazineAmmoLoader |
MechanicalPowerTransmission |
MechanicalWings |
MentalScreen |
Mill |
MissilePerformance |
Mod* (IModification, which all mods inherit, is an IActivePart) |
MultiIntPropertyChanger |
MultiNavigationBonus |
NavigationBonus |
NightVision |
NoKnockdown |
PartsGas |
PointDefense |
PowerCord |
PoweredFloating |
PowerOutlet |
PowerSwitch |
ProgrammableRecoiler |
RadiusEventSender |
RealityStabilization |
ReclamationCist |
RecoilOnDeath |
ReduceCooldowns |
ReduceEnergyCosts |
ReflectProjectiles |
RemotePowerSwitch |
RespondToEvent |
RocketSkates |
SapChargeOnHit |
SaveModifier |
SaveModifiers |
SlottedCellCharger |
Smartgun |
SolarArray |
Stopsvaalinn |
StunOnHit |
Suspensor |
TattooGun |
Teleporter |
TeleporterPair |
TemperatureAdjuster |
TemplarPhylactery |
Toolbox |
UniversalCharger |
UrbanCamouflage |
VampiricWeapon |
VibroWeapon |
WindTurbine |
Windup |
ZeroPointEnergyCollector |
ZoneAdjust |