2026-06-13 04:56:50 +01:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
2020-12-22 03:13:43 +00:00
|
|
|
* 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;
|
2020-04-09 10:33:16 +01:00
|
|
|
using System.Collections;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2026-06-21 09:54:15 +01:00
|
|
|
using GameCult.Aetheria.State.Verse;
|
2026-06-21 09:37:54 +01:00
|
|
|
using static CultMath.math;
|
|
|
|
|
using cfloat3 = CultMath.float3;
|
|
|
|
|
using cfloat4 = CultMath.float4;
|
2020-04-09 10:33:16 +01:00
|
|
|
|
2021-04-18 05:13:20 +01:00
|
|
|
public abstract class Behavior
|
2020-04-09 10:33:16 +01:00
|
|
|
{
|
2026-06-15 17:38:55 +01:00
|
|
|
private readonly Dictionary<string, PerformanceStat> _performanceStats;
|
2026-06-14 17:23:27 +01:00
|
|
|
public string Kind { get; }
|
2026-06-14 17:27:16 +01:00
|
|
|
public int Group { get; }
|
2021-04-18 05:13:20 +01:00
|
|
|
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;
|
|
|
|
|
|
2026-06-21 09:37:54 +01:00
|
|
|
public cfloat3 Direction
|
2021-04-18 05:13:20 +01:00
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if(Item != null)
|
|
|
|
|
{
|
|
|
|
|
var hardpoint = Entity.Hardpoints[Item.Position.x, Item.Position.y];
|
2026-06-13 16:31:45 +01:00
|
|
|
if (hardpoint != null && Entity.HardpointTransforms.TryGetValue(hardpoint, out var hardpointTransform))
|
2021-04-18 05:13:20 +01:00
|
|
|
{
|
2026-06-13 16:31:45 +01:00
|
|
|
return normalize(hardpointTransform.direction);
|
2021-04-18 05:13:20 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2026-06-21 09:37:54 +01:00
|
|
|
var itemDirection = AetheriaMath.Rotate(Entity.CultDirection, Item.EquippableItem.Rotation);
|
|
|
|
|
return new cfloat3(itemDirection.x, 0, itemDirection.y);
|
2021-04-18 05:13:20 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 09:37:54 +01:00
|
|
|
var entityDirection = Entity.CultDirection;
|
|
|
|
|
return new cfloat3(entityDirection.x, 0, entityDirection.y);
|
2021-04-18 05:13:20 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 18:51:59 +01:00
|
|
|
protected Behavior(RuntimeBehaviorDefinition definition, EquippedItem item)
|
|
|
|
|
{
|
|
|
|
|
_performanceStats = new Dictionary<string, PerformanceStat>(StringComparer.Ordinal);
|
|
|
|
|
Kind = definition?.Kind ?? "";
|
|
|
|
|
Group = definition?.Group ?? 0;
|
|
|
|
|
Item = item;
|
|
|
|
|
ItemManager = Item.ItemManager;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected Behavior(RuntimeBehaviorDefinition definition, ConsumableItemEffect consumable)
|
|
|
|
|
{
|
|
|
|
|
_performanceStats = new Dictionary<string, PerformanceStat>(StringComparer.Ordinal);
|
|
|
|
|
Kind = definition?.Kind ?? "";
|
|
|
|
|
Group = definition?.Group ?? 0;
|
|
|
|
|
Consumable = consumable;
|
|
|
|
|
ItemManager = consumable.Entity.ItemManager;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-18 05:13:20 +01:00
|
|
|
public float Evaluate(PerformanceStat stat) => Item?.Evaluate(stat) ?? Consumable.Evaluate(stat);
|
2026-06-15 17:01:03 +01:00
|
|
|
|
2026-06-21 09:37:54 +01:00
|
|
|
public float Evaluate(PerformanceStat stat, StatConditionMask condition, float value) =>
|
|
|
|
|
Item?.Evaluate(stat, condition, value) ?? Consumable.Evaluate(stat, condition, value);
|
|
|
|
|
|
|
|
|
|
public float Evaluate(
|
|
|
|
|
PerformanceStat stat,
|
|
|
|
|
StatConditionMask firstCondition,
|
|
|
|
|
float firstValue,
|
|
|
|
|
StatConditionMask secondCondition,
|
|
|
|
|
float secondValue) =>
|
|
|
|
|
Item?.Evaluate(stat, firstCondition, firstValue, secondCondition, secondValue) ??
|
|
|
|
|
Consumable.Evaluate(stat, firstCondition, firstValue, secondCondition, secondValue);
|
|
|
|
|
|
2026-06-15 17:01:03 +01:00
|
|
|
public bool TryGetPerformanceStat(string statName, out PerformanceStat stat)
|
|
|
|
|
{
|
2026-06-15 18:51:59 +01:00
|
|
|
if (!string.IsNullOrWhiteSpace(statName) &&
|
|
|
|
|
_performanceStats.TryGetValue(statName, out stat))
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stat = null;
|
|
|
|
|
return false;
|
2026-06-15 17:38:55 +01:00
|
|
|
}
|
2026-06-15 17:01:03 +01:00
|
|
|
|
2026-06-15 19:06:13 +01:00
|
|
|
protected void RegisterPerformanceStat(string statName, PerformanceStat stat)
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(statName) && stat != null)
|
|
|
|
|
{
|
|
|
|
|
_performanceStats[statName] = stat;
|
2026-06-21 09:37:54 +01:00
|
|
|
Item?.RegisterPerformanceStat(Kind, statName, stat);
|
2026-06-15 19:06:13 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-18 05:13:20 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-06-13 04:56:50 +01:00
|
|
|
|
2021-04-18 05:13:20 +01:00
|
|
|
public virtual bool Execute(float dt)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2020-04-09 10:33:16 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-18 05:13:20 +01:00
|
|
|
public interface IActivatedBehavior
|
2021-01-25 05:53:36 +00:00
|
|
|
{
|
|
|
|
|
void Activate();
|
|
|
|
|
void Deactivate();
|
|
|
|
|
}
|
2020-04-09 10:33:16 +01:00
|
|
|
|
2021-04-18 05:13:20 +01:00
|
|
|
public interface IAnalogBehavior
|
2020-04-09 10:33:16 +01:00
|
|
|
{
|
2020-06-05 04:16:33 +01:00
|
|
|
float Axis { get; set; }
|
2020-04-09 10:33:16 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-21 01:55:25 +00:00
|
|
|
public interface IEventBehavior
|
|
|
|
|
{
|
|
|
|
|
void ResetEvents();
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-12 01:42:13 +01:00
|
|
|
public interface IInitializableBehavior
|
|
|
|
|
{
|
|
|
|
|
void Initialize();
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-25 05:53:36 +00:00
|
|
|
public interface IInteractiveBehavior
|
|
|
|
|
{
|
|
|
|
|
bool Exposed { get; }
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-12 01:42:13 +01:00
|
|
|
public interface IAlwaysUpdatedBehavior
|
|
|
|
|
{
|
2021-01-10 03:59:47 +00:00
|
|
|
void Update(float delta);
|
2020-05-12 01:42:13 +01:00
|
|
|
}
|
|
|
|
|
|
2020-12-31 19:53:16 +00:00
|
|
|
public interface IProgressBehavior
|
|
|
|
|
{
|
|
|
|
|
float Progress { get; }
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-10 23:20:38 +00:00
|
|
|
public interface IOrderedBehavior
|
|
|
|
|
{
|
|
|
|
|
int Order { get; }
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-08 01:01:33 +01:00
|
|
|
public interface IPopulationAssignment
|
|
|
|
|
{
|
|
|
|
|
int AssignedPopulation { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 18:51:59 +01:00
|
|
|
public sealed class RuntimeBehaviorDefinition
|
|
|
|
|
{
|
2026-06-15 19:06:13 +01:00
|
|
|
private readonly IReadOnlyDictionary<int, AetheriaRuntimeBehaviorValue> _fields;
|
|
|
|
|
|
2026-06-15 18:51:59 +01:00
|
|
|
public string Kind { get; }
|
|
|
|
|
public int Group { get; }
|
|
|
|
|
|
|
|
|
|
public RuntimeBehaviorDefinition(AetheriaRuntimeBehaviorPayload payload)
|
|
|
|
|
{
|
|
|
|
|
Kind = payload?.Kind ?? "";
|
|
|
|
|
Group = payload?.Group ?? 0;
|
2026-06-15 19:06:13 +01:00
|
|
|
|
|
|
|
|
var fields = new Dictionary<int, AetheriaRuntimeBehaviorValue>();
|
|
|
|
|
if (payload != null)
|
|
|
|
|
{
|
|
|
|
|
foreach (var field in payload.Fields)
|
|
|
|
|
{
|
|
|
|
|
if (!fields.ContainsKey(field.Key))
|
|
|
|
|
{
|
|
|
|
|
fields.Add(field.Key, field.Value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_fields = fields;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Bool(int key, bool fallback = false)
|
|
|
|
|
{
|
|
|
|
|
return _fields.TryGetValue(key, out var value) ? value.BoolValue : fallback;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 19:20:06 +01:00
|
|
|
public float Float(int key, float fallback = 0)
|
|
|
|
|
{
|
|
|
|
|
return _fields.TryGetValue(key, out var value) ? (float)value.NumberValue : fallback;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 20:12:34 +01:00
|
|
|
public int Int(int key, int fallback = 0)
|
|
|
|
|
{
|
|
|
|
|
return _fields.TryGetValue(key, out var value) ? checked((int)value.NumberValue) : fallback;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 19:54:54 +01:00
|
|
|
public uint UInt(int key, uint fallback = 0)
|
|
|
|
|
{
|
|
|
|
|
return _fields.TryGetValue(key, out var value) ? checked((uint)value.NumberValue) : fallback;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 09:37:54 +01:00
|
|
|
public cfloat3 Float3(int key, cfloat3 fallback = default)
|
2026-06-15 19:54:54 +01:00
|
|
|
{
|
|
|
|
|
return _fields.TryGetValue(key, out var value) ? ToFloat3(value) : fallback;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 19:20:06 +01:00
|
|
|
public string String(int key, string fallback = "")
|
|
|
|
|
{
|
|
|
|
|
return _fields.TryGetValue(key, out var value) ? value.StringValue ?? "" : fallback;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 19:06:13 +01:00
|
|
|
public string ItemKey(int key, string fallback = "")
|
|
|
|
|
{
|
|
|
|
|
return _fields.TryGetValue(key, out var value) &&
|
|
|
|
|
!string.IsNullOrWhiteSpace(value.ItemKeyValue)
|
|
|
|
|
? value.ItemKeyValue
|
|
|
|
|
: fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public PerformanceStat PerformanceStat(int key, PerformanceStat fallback)
|
|
|
|
|
{
|
2026-06-21 09:37:54 +01:00
|
|
|
return _fields.TryGetValue(key, out var value)
|
|
|
|
|
? AetheriaRuntimeBehaviorValueReader.ReadPerformanceStat(value)
|
|
|
|
|
: fallback;
|
2026-06-15 19:06:13 +01:00
|
|
|
}
|
|
|
|
|
|
2026-06-15 19:29:34 +01:00
|
|
|
public BezierCurve BezierCurve(int key, BezierCurve fallback)
|
|
|
|
|
{
|
|
|
|
|
if (!_fields.TryGetValue(key, out var value))
|
|
|
|
|
{
|
|
|
|
|
return fallback;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 09:37:54 +01:00
|
|
|
return global::BezierCurve.FromKeys(value.Children.Count > 0
|
|
|
|
|
? value.Children[0].Children.Select(ToBezierKey)
|
|
|
|
|
: Enumerable.Empty<(float, float, float, float)>());
|
2026-06-15 19:29:34 +01:00
|
|
|
}
|
|
|
|
|
|
2026-06-21 09:37:54 +01:00
|
|
|
public cfloat4[] Float4Array(int key, cfloat4[] fallback)
|
2026-06-15 20:12:34 +01:00
|
|
|
{
|
|
|
|
|
return _fields.TryGetValue(key, out var value)
|
|
|
|
|
? value.Children.Select(ToFloat4).ToArray()
|
|
|
|
|
: fallback;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 19:29:34 +01:00
|
|
|
public StatReference StatReference(int key, StatReference fallback)
|
|
|
|
|
{
|
|
|
|
|
if (!_fields.TryGetValue(key, out var value))
|
|
|
|
|
{
|
|
|
|
|
return fallback;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 09:37:54 +01:00
|
|
|
return AetheriaRuntimeBehaviorValueReader.ReadStatReference(value);
|
2026-06-15 19:29:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public T Enum<T>(int key, T fallback) where T : struct
|
|
|
|
|
{
|
2026-06-21 09:37:54 +01:00
|
|
|
return _fields.TryGetValue(key, out var value)
|
|
|
|
|
? AetheriaRuntimeBehaviorValueReader.ReadEnum(value, fallback)
|
|
|
|
|
: fallback;
|
2026-06-15 19:29:34 +01:00
|
|
|
}
|
|
|
|
|
|
2026-06-21 09:37:54 +01:00
|
|
|
private static cfloat4 ToFloat4(AetheriaRuntimeBehaviorValue value)
|
2026-06-15 19:06:13 +01:00
|
|
|
{
|
2026-06-21 09:37:54 +01:00
|
|
|
return new cfloat4(ChildFloat(value, 0), ChildFloat(value, 1), ChildFloat(value, 2), ChildFloat(value, 3));
|
2026-06-15 19:06:13 +01:00
|
|
|
}
|
|
|
|
|
|
2026-06-21 09:37:54 +01:00
|
|
|
private static (float, float, float, float) ToBezierKey(AetheriaRuntimeBehaviorValue value)
|
2026-06-15 19:29:34 +01:00
|
|
|
{
|
2026-06-21 09:37:54 +01:00
|
|
|
return (ChildFloat(value, 0), ChildFloat(value, 1), ChildFloat(value, 2), ChildFloat(value, 3));
|
2026-06-15 19:29:34 +01:00
|
|
|
}
|
|
|
|
|
|
2026-06-21 09:37:54 +01:00
|
|
|
private static cfloat3 ToFloat3(AetheriaRuntimeBehaviorValue value)
|
2026-06-15 19:54:54 +01:00
|
|
|
{
|
2026-06-21 09:37:54 +01:00
|
|
|
return new cfloat3(ChildFloat(value, 0), ChildFloat(value, 1), ChildFloat(value, 2));
|
2026-06-15 19:54:54 +01:00
|
|
|
}
|
|
|
|
|
|
2026-06-15 19:06:13 +01:00
|
|
|
private static float ChildFloat(AetheriaRuntimeBehaviorValue value, int index)
|
|
|
|
|
{
|
|
|
|
|
return value.Children.Count > index ? (float)value.Children[index].NumberValue : 0;
|
2026-06-15 18:51:59 +01:00
|
|
|
}
|
2026-06-15 19:29:34 +01:00
|
|
|
|
|
|
|
|
private static string ChildString(AetheriaRuntimeBehaviorValue value, int index)
|
|
|
|
|
{
|
|
|
|
|
return value.Children.Count > index ? value.Children[index].StringValue ?? "" : "";
|
|
|
|
|
}
|
2026-06-21 09:37:54 +01:00
|
|
|
|
|
|
|
|
private static AetheriaRuntimeBehaviorValue ChildValue(AetheriaRuntimeBehaviorValue value, int index)
|
|
|
|
|
{
|
|
|
|
|
return value != null && value.Children.Count > index ? value.Children[index] : null;
|
|
|
|
|
}
|
2026-06-15 18:51:59 +01:00
|
|
|
}
|