/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ using System; using System.Collections; using System.Collections.Generic; using System.Linq; using MessagePack; using Newtonsoft.Json; using CultMath; using static CultMath.math; public abstract class Behavior { public BehaviorData Data { get; } public EquippedItem Item { get; } private ConsumableItemEffect Consumable { get; } protected ItemManager ItemManager { get; } protected Entity Entity => Item?.Entity ?? Consumable.Entity; public float Temperature => Item?.Temperature ?? Consumable.Entity.MaxTemp; public float3 Direction { get { if(Item != null) { var itemDirection = Entity.Direction.Rotate(Item.EquippableItem.Rotation); return float3(itemDirection.x, 0, itemDirection.y); } return float3(Entity.Direction.x, 0, Entity.Direction.y); } } protected Behavior(BehaviorData data, EquippedItem item) { Data = data; Item = item; ItemManager = Item.ItemManager; } protected Behavior(BehaviorData data, ConsumableItemEffect consumable) { Data = data; Consumable = consumable; ItemManager = consumable.Entity.ItemManager; } public float Evaluate(PerformanceStat stat) => Item?.Evaluate(stat) ?? Consumable.Evaluate(stat); // Nominal-request ruling (docs/stats-and-power-cut.md, operator ruling 2026-09-19): the read a PowerRequest // implementation uses for a stat named in StatValidation.PowerRequestFields, in place of Evaluate above. A // consumable-hosted instance (Item == null) never reaches PowerBus.Step at all (see e.g. EnergyDraw.Execute's // own comment), and ConsumableItemEffect.PowerSupplyFactor is already the identity (1) unconditionally, so // Consumable.Evaluate is already nominal with respect to power and needs no separate context. public float EvaluateNominalPower(PerformanceStat stat) => Item?.EvaluateNominalPower(stat) ?? Consumable.Evaluate(stat); protected void AddHeat(float heat) => Item?.AddHeat(heat); // TODO: Heat for Consumables protected void CauseDamage(float damage) { if (Item != null) { Item.EquippableItem.Durability -= damage; Entity.ItemDamage.OnNext((Item, damage)); } else { Consumable.Entity.Hull.Durability -= damage; Consumable.Entity.HullDamage.OnNext(damage); } } protected void CauseWearDamage(float multiplier) { if (Item != null) { CauseDamage(Item.Wear * multiplier); } } public virtual bool Execute(float dt) { return true; } } public interface IActivatedBehavior { void Activate(); void Deactivate(); } public interface IAnalogBehavior { float Axis { get; set; } } public interface IEventBehavior { void ResetEvents(); } public interface IInitializableBehavior { void Initialize(); } public interface IInteractiveBehavior { bool Exposed { get; } } public interface IAlwaysUpdatedBehavior { void Update(float delta); } public interface IProgressBehavior { float Progress { get; } } public interface IPopulationAssignment { int AssignedPopulation { get; set; } } // Cut 3 (docs/stats-and-power-cut.md §1.2): a behaviour that wants to draw from its entity's PowerBus declares a // request instead of spending energy itself. PowerBus.Step calls this on every consumer once per tick, before // any behaviour's Execute runs, so PowerRequest must be computable from state that is already current at that // point -- a resolved stat and, where the map calls for one, a behaviour-supplied scalar such as Thruster's // throttle axis (set externally before the tick) -- never from work Execute itself would otherwise do. The // behaviour reads back how much of its own request was granted from Item.PowerSupply during its own Execute. // // Cut 5 (docs/stats-and-power-cut.md §1.3): every kind of consumer also names the priority tier (PowerTiers.cs) // it defaults to. EquippedItem reads this exactly once, to seed EquippableItem.PowerTier the first time an item // without a player-chosen tier gets equipped -- after that the stored field is what PowerBus reads, never this. public interface IPowerConsumer { float PowerRequest(float dt); int DefaultPowerTier { get; } } [Inspectable, Union(0, typeof(GuidedWeaponData)), Union(1, typeof(LauncherData)), Union(2, typeof(ReactorData)), Union(3, typeof(RadiatorData)), Union(4, typeof(StatModifierData)), Union(5, typeof(SensorData)), Union(6, typeof(ReflectorData)), Union(7, typeof(ShieldData)), Union(8, typeof(ThrusterData)), Union(9, typeof(WearData)), Union(10, typeof(VelocityConversionData)), Union(11, typeof(VelocityLimitData)), // Union(12): retired, do not reuse -- was AetherDriveData (retire-longinusx; parked/aether-drive) // Union(14, typeof(TowingControllerData)), Union(15, typeof(CooldownData)), Union(16, typeof(HeatData)), // Union(17, typeof(HitscanData)), Union(18, typeof(ItemUsageData)), //Union(19, typeof(RadianceData)), Union(20, typeof(SwitchData)), Union(21, typeof(TriggerData)), Union(22, typeof(VisibilityData)), Union(23, typeof(ThermotoggleData)), Union(24, typeof(EnergyDrawData)), // Union(25, typeof(MiningControllerData)), // Union(26): retired, do not reuse -- was the "hit it with a tool" behavior deleted in Cut 2 (docs/mining-cut.md, Q1=A). // Union(27, typeof(SurveyControllerData)), Union(28, typeof(ResourceScannerData)), // Union(29, typeof(WanderControllerData)), // Union(30, typeof(HaulingControllerData)), Union(31, typeof(CapacitorData)), Union(32, typeof(CockpitData)), Union(33, typeof(HeatStorageData)), Union(34, typeof(TurretControllerData)), Union(35, typeof(InstantWeaponData)), Union(36, typeof(ConstantWeaponData)), Union(37, typeof(ChargedWeaponData)), Union(38, typeof(AutoWeaponData)), // Cut 2 (docs/fire-control-cut.md): 39 is the next free index against this list, not the 39 the // superseded draft quoted -- checked directly here rather than trusted from that document. Union(39, typeof(TargetingSystemData)), Union(40, typeof(MineLayerData)), Union(41, typeof(ThrottleLockData)), Union(42, typeof(VapourDumpData)), JsonObject(MemberSerialization.OptIn)] public abstract class BehaviorData { [Inspectable, JsonProperty("group"), Key(0)] public int Group; public abstract Behavior CreateInstance(EquippedItem item); public abstract Behavior CreateInstance(ConsumableItemEffect consumable); public override string ToString() { return base.ToString().FormatTypeName(); } }