launch a new base system for codex perception

This commit is contained in:
Nilton Constantino 2026-07-04 13:18:24 +01:00
parent 80cf81a451
commit d620c54be7
No known key found for this signature in database
61 changed files with 17727 additions and 16625 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0da99a62443b843fa898402c2a4b02c8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1a9a986ab47dc4b528fa8a7bcf5e7ced
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,15 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b6b70c957a7948bc8f0be6f30e791f18, type: 3}
m_Name: Codex - Concept Library
m_EditorClassIdentifier: Gameplay::Intrepid.Gameplay.Codex.Knowledge.CodexConceptLibrary
concepts: []

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 63a5d7be4117a4007af1ab89f0050093
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 709db9526442d45a381fafe2b54ff976
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,15 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 73c8da23e167c437b9b4044376377a24, type: 3}
m_Name: '# Codex - Pictorial Protocol Library'
m_EditorClassIdentifier: Gameplay::Intrepid.Gameplay.Codex.Visual.PictorialProtocolLibrary
protocols: []

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3d9e05a934e384073975b2763edfba69
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,40 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b9a2dd3bc25546bfb126b9445e977c3c, type: 3}
m_Name: pictorial-protocol.default
m_EditorClassIdentifier: Gameplay::Intrepid.Gameplay.Codex.Visual.PictorialProtocolTag
code: dcfda51c61c345898e10ae7aeae468ce
localization: {fileID: 0}
description: {fileID: 0}
interfaceStyle:
surfaceBase: {r: 0.02745098, g: 0.0627451, b: 0.078431375, a: 1}
surfaceRaised: {r: 0.0627451, g: 0.1254902, b: 0.14901961, a: 1}
surfaceDeep: {r: 0.011764706, g: 0.02745098, b: 0.03137255, a: 1}
surfaceOverlay: {r: 0.08627451, g: 0.16862746, b: 0.2, a: 0.8}
textPrimary: {r: 0.84705883, g: 0.96862745, b: 1, a: 1}
textSecondary: {r: 0.56078434, g: 0.78039217, b: 0.81960785, a: 1}
textMuted: {r: 0.32156864, g: 0.4745098, b: 0.50980395, a: 1}
textDisabled: {r: 0.18431373, g: 0.27450982, b: 0.29803923, a: 1}
focus: {r: 0.36862746, g: 0.92156863, b: 1, a: 1}
selected: {r: 0.6156863, g: 1, b: 0.9019608, a: 1}
disabled: {r: 0.23137255, g: 0.33333334, b: 0.36078432, a: 1}
denied: {r: 1, g: 0.41568628, b: 0.2901961, a: 1}
warning: {r: 1, g: 0.81960785, b: 0.4, a: 1}
danger: {r: 1, g: 0.20784314, b: 0.3647059, a: 1}
corruption: {r: 0.7058824, g: 0.36078432, b: 1, a: 1}
life: {r: 1, g: 0.23137255, b: 0.30980393, a: 1}
shield: {r: 0.21960784, g: 0.8509804, b: 1, a: 1}
cadence: {r: 0.9647059, g: 0.9529412, b: 0.8235294, a: 1}
progress: {r: 0.6156863, g: 1, b: 0.9019608, a: 1}
lineSubtle: {r: 0.16862746, g: 0.3372549, b: 0.3764706, a: 1}
lineActive: {r: 0.36862746, g: 0.92156863, b: 1, a: 1}
lineCritical: {r: 1, g: 0.20784314, b: 0.3647059, a: 1}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6c3fd2d4d883248e18470247e672317a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d51db36b0b09472f80dfbd4d2d72088d
timeCreated: 1783111087

View File

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using Intrepid.GameManagers;
using Intrepid.Gameplay.Codex.Knowledge;
using Intrepid.Gameplay.Codex.Knowledge.Events;
using Intrepid.Gameplay.Codex.Visual;
using Intrepid.Gameplay.Codex.Visual.Events;
using Intrepid.SimpleEvents;
using UnityEngine;
namespace Intrepid.Gameplay.Codex
{
/// <summary>
/// Runtime owner of what the CODEX currently knows.
/// This is intentionally separated from PDA runtime state.
/// </summary>
public class CodexRuntime : EventListener
{
[SerializeField] private CodexOntology ontology = new();
[SerializeField] private PictorialProtocolInventory pictorialProtocols = new();
public CodexOntology Ontology => ontology;
public PictorialProtocolInventory PictorialProtocols => pictorialProtocols;
public IReadOnlyList<CodexConceptTag> KnownConcepts => ontology.KnownConcepts;
public IReadOnlyList<PictorialProtocolTag> KnownPictorialProtocols => pictorialProtocols.KnownProtocols;
public bool Knows(CodexConceptTag concept) => ontology.Knows(concept);
public bool Knows(PictorialProtocolTag protocol) => pictorialProtocols.Knows(protocol);
protected override void InitializeVariables()
{
}
protected override void OnBootstrap()
{
}
protected override void Subscriptions()
{
}
public void Learn(CodexConceptTag concept, bool raiseEvent = true)
{
if (concept.IsNull()) return;
if (Knows(concept)) return;
ontology.Learn(concept);
if (!raiseEvent) return;
Proxy.Instance.EventBus.Raise(new CodexConceptLearnedEvent
{
Concept = concept,
});
}
public void Unlock(PictorialProtocolTag protocol, bool raiseEvent = true)
{
if (protocol.IsNull()) return;
if (Knows(protocol)) return;
pictorialProtocols.Unlock(protocol);
if (!raiseEvent) return;
Proxy.Instance.EventBus.Raise(new PictorialProtocolUnlockedEvent
{
Protocol = protocol,
});
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7a91d3e76c034a31875d4a4ae36af980

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4e243cdc715848c6aaebd2dfd4f5f5d9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Intrepid.Utilities;
using Sirenix.OdinInspector;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace Intrepid.Gameplay.Codex.Knowledge
{
[Serializable]
[CreateAssetMenu(fileName = "Codex - Concept Library", menuName = "GON/CODEX/Knowledge/Concept Library")]
public class CodexConceptLibrary : SingletonScriptableObject<CodexConceptLibrary>
{
[GroupInjections, SerializeField] private List<CodexConceptTag> concepts = new();
public IReadOnlyList<CodexConceptTag> Concepts => concepts.Where(c => c.IsNotNull()).Distinct().ToList();
public bool Contains(CodexConceptTag concept)
{
return concept.IsNotNull() && concepts.Contains(concept);
}
#if UNITY_EDITOR
[PropertyOrder(-10)]
[GroupInjections, Button(ButtonSizes.Large), GUIColor(0.35f, 0.8f, .35f)]
protected void FindAll()
{
concepts.Clear();
var guids = AssetDatabase.FindAssets($"t:{nameof(CodexConceptTag)}");
foreach (var guid in guids)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
var protocol = AssetDatabase.LoadAssetAtPath<CodexConceptTag>(path);
concepts.Add(protocol);
}
EditorUtility.SetDirty(this);
}
#endif
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b6b70c957a7948bc8f0be6f30e791f18

View File

@ -0,0 +1,19 @@
using System;
using Intrepid.Localization;
using Intrepid.TagSystem;
using Intrepid.Utilities;
using UnityEngine;
namespace Intrepid.Gameplay.Codex.Knowledge
{
[Serializable]
[CreateAssetMenu(fileName = "New Concept Tag", menuName = "GON/CODEX/Knowledge/Concept Tag")]
public class CodexConceptTag : Tag
{
[GroupConfigurations, SerializeField] private LocalizationTag localization;
[GroupConfigurations, SerializeField] private LocalizationTag description;
public LocalizationTag Localization => localization;
public LocalizationTag Description => description;
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 19f2c1dad39848b8847bf424fc7605b2

View File

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Intrepid.Utilities;
using Sirenix.OdinInspector;
using UnityEngine;
namespace Intrepid.Gameplay.Codex.Knowledge
{
[Serializable]
[GroupConfigurations("Codex Ontology"), HideLabel]
public class CodexOntology
{
[SerializeField] private List<CodexConceptTag> knownConcepts = new();
public IReadOnlyList<CodexConceptTag> KnownConcepts => knownConcepts.Where(c => c.IsNotNull()).Distinct().ToList();
public bool Knows(CodexConceptTag concept)
{
return concept.IsNotNull() && knownConcepts.Contains(concept);
}
public bool Learn(CodexConceptTag concept)
{
if (concept.IsNull()) return false;
if (knownConcepts.Contains(concept)) return false;
knownConcepts.Add(concept);
return true;
}
public bool Forget(CodexConceptTag concept)
{
if (concept.IsNull()) return false;
return knownConcepts.Remove(concept);
}
#if UNITY_EDITOR
[GroupDebug, Button(ButtonSizes.Medium)]
private void CleanupKnownConcepts()
{
knownConcepts = knownConcepts.Where(c => c.IsNotNull()).Distinct().ToList();
}
#endif
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: cb4eda8c15124aa38685c8c2b1ac9207

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0498589829b124127bc6c22e027ec669
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
using Intrepid.SimpleEvents;
namespace Intrepid.Gameplay.Codex.Knowledge.Events
{
public class CodexConceptLearnedEvent : IEventBase
{
public CodexConceptTag Concept { get; set; }
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 79e533105f1774b95ad5bacdd1887aff

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e7c13a24868ef417095514def91bb5d9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,14 @@
namespace Intrepid.Gameplay.Codex.Perception
{
public readonly struct CodexInterpretationRequest
{
public CodexReadable Readable { get; }
public CodexReadingState ReadingState { get; }
public CodexInterpretationRequest(CodexReadable readable, CodexReadingState readingState)
{
Readable = readable;
ReadingState = readingState;
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 76e9ce38e022448d0b3c75ba5bd6e193

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
using Intrepid.Gameplay.Codex.Knowledge;
namespace Intrepid.Gameplay.Codex.Perception
{
public class CodexInterpretationResult
{
public CodexReadable Readable { get; set; }
public CodexReadingState ReadingState { get; set; }
public IReadOnlyList<CodexConceptTag> RecognizedConcepts { get; set; }
public IReadOnlyList<CodexConceptTag> ResidueConcepts { get; set; }
public float Confidence { get; set; }
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 52b61255208554314b5f8d5a35d2120a

View File

@ -0,0 +1,16 @@
using System;
using Intrepid.Gameplay.Codex.Knowledge;
using UnityEngine;
namespace Intrepid.Gameplay.Codex.Perception
{
[Serializable]
public class CodexManifestEntry
{
[SerializeField] private CodexConceptTag concept;
[SerializeField, Range(0f, 1f)] private float weight = 1f;
public CodexConceptTag Concept => concept;
public float Weight => weight;
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 558dfbb5de4c54f70aa54dc15ce09cb5

View File

@ -0,0 +1,26 @@
using System.Collections.Generic;
using Intrepid.Utilities;
using UnityEngine;
namespace Intrepid.Gameplay.Codex.Perception
{
/// <summary>
/// Marker for world objects that can be processed by the CODEX Reading Field.
/// Keep this data-oriented. Gameplay interaction should live elsewhere.
/// </summary>
public class CodexReadable : MonoBehaviour
{
[GroupConfigurations, SerializeField] private string semanticId;
[GroupConfigurations, SerializeField] private List<CodexManifestEntry> manifest = new();
[GroupDebug, SerializeField] private CodexReadingState readingState = CodexReadingState.Unseen;
public string SemanticId => semanticId;
public IReadOnlyList<CodexManifestEntry> Manifest => manifest;
public CodexReadingState ReadingState => readingState;
public void SetReadingState(CodexReadingState state)
{
readingState = state;
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 74300c9a0423e42fa8bb8b5ea28a4ff8

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using Intrepid.Gameplay.Codex.Knowledge;
using Intrepid.Utilities;
using UnityEngine;
namespace Intrepid.Gameplay.Codex.Perception
{
/// <summary>
/// Minimal trigger-based reading field. Shape/orientation should be authored by the collider on the GameObject.
/// Later this can become a true forward cone/frustum query.
/// </summary>
[RequireComponent(typeof(Collider))]
public class CodexReadingField : MonoBehaviour
{
[GroupConfigurations, SerializeField] private CodexRuntime knowledge;
[GroupDebug, SerializeField] private List<CodexReadable> readablesInside = new();
private CodexSemanticClassifier classifier;
public IReadOnlyList<CodexReadable> ReadablesInside => readablesInside;
public event Action<CodexInterpretationResult> ReadableDetected;
private void Awake()
{
classifier = new CodexSemanticClassifier(knowledge);
}
private void OnTriggerEnter(Collider other)
{
var readable = other.GetComponentInParent<CodexReadable>();
if (readable.IsNull()) return;
if (readablesInside.Contains(readable)) return;
readablesInside.Add(readable);
readable.SetReadingState(CodexReadingState.Detected);
if (knowledge.IsNull()) return;
var result = classifier.Classify(new CodexInterpretationRequest(readable, readable.ReadingState));
ReadableDetected?.Invoke(result);
}
private void OnTriggerExit(Collider other)
{
var readable = other.GetComponentInParent<CodexReadable>();
if (readable.IsNull()) return;
readablesInside.Remove(readable);
readable.SetReadingState(CodexReadingState.Unseen);
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e3a9bcb1c030f4d83b95174465c45dd9

View File

@ -0,0 +1,15 @@
namespace Intrepid.Gameplay.Codex.Perception
{
public enum CodexReadingState
{
Unseen,
Detected,
Scanning,
Known,
Focused,
Relevant,
Locked,
Reinterpreted,
Corrupted
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 22e041dc3b2d54f18b99b74a2c455061

View File

@ -0,0 +1,36 @@
using System.Linq;
using Intrepid.Gameplay.Codex.Knowledge;
namespace Intrepid.Gameplay.Codex.Perception
{
/// <summary>
/// First deterministic placeholder for the future vector/prototype classifier.
/// Today it only separates known concepts from semantic residue.
/// </summary>
public class CodexSemanticClassifier
{
private readonly CodexRuntime knowledge;
public CodexSemanticClassifier(CodexRuntime knowledge)
{
this.knowledge = knowledge;
}
public CodexInterpretationResult Classify(CodexInterpretationRequest request)
{
var manifest = request.Readable.Manifest.Where(e => e.Concept != null).ToList();
var recognized = manifest.Where(e => knowledge.Knows(e.Concept)).Select(e => e.Concept).Distinct().ToList();
var residue = manifest.Where(e => !knowledge.Knows(e.Concept)).Select(e => e.Concept).Distinct().ToList();
var confidence = manifest.Count == 0 ? 0f : (float)recognized.Count / manifest.Count;
return new CodexInterpretationResult
{
Readable = request.Readable,
ReadingState = request.ReadingState,
RecognizedConcepts = recognized,
ResidueConcepts = residue,
Confidence = confidence
};
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 188e808f1aaf1470c9b6c596a4965724

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 518b37a61206f41d98dd2d4a85b008f8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,32 @@
using Intrepid.Gameplay.Codex.Visual;
using Intrepid.Gameplay.Codex.Visual.Events;
using Intrepid.SimpleEvents;
namespace Intrepid.Gameplay.Codex.Perception.Presentation
{
/// <summary>
/// Tiny base for future diegetic marks. It only listens to visual changes.
/// Specific marks can inherit and apply materials, sprites, particles or line renderers.
/// </summary>
public abstract class CodexWorldMarkPresenter : EventListener
{
protected override void InitializeVariables()
{
}
protected override void OnBootstrap()
{
ApplyVisual(CodexVisualService.Instance.CurrentContext);
}
protected override void Subscriptions()
{
Subscribe<CodexVisualChangedEvent>(e =>
{
ApplyVisual(e.Context);
});
}
protected abstract void ApplyVisual(CodexVisualContext context);
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a53fd6f73fe094a94957f1fbd7d053ec

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c379a4bf2286d4cc497923ae4177cfb4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,10 @@
namespace Intrepid.Gameplay.Codex.Visual
{
public enum CodexVisualChangeMode
{
Instant,
Preview,
Commit,
CancelPreview
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8f5c4907cbeb64120a1e148ea392e38f

View File

@ -0,0 +1,19 @@
namespace Intrepid.Gameplay.Codex.Visual
{
/// <summary>
/// Runtime-resolved visual context. Keep this immutable-ish and cheap to pass around.
/// </summary>
public class CodexVisualContext
{
public PictorialProtocolTag Protocol { get; }
public int Version { get; }
public bool IsPreview { get; }
public CodexVisualContext(PictorialProtocolTag protocol, int version, bool isPreview = false)
{
Protocol = protocol;
Version = version;
IsPreview = isPreview;
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7cfc6e6f636334e1f9282ac9e028c900

View File

@ -0,0 +1,77 @@
using Intrepid.GameManagers;
using Intrepid.Gameplay.Codex.Visual.Events;
using Intrepid.Utilities;
using UnityEngine;
namespace Intrepid.Gameplay.Codex.Visual
{
/// <summary>
/// New visual-language entrypoint for CODEX/PDA/ActionBelt/world marks.
/// Transitional note: this currently uses the existing Theme asset.
/// Later Theme can be renamed/wrapped as PictorialProtocol without changing consumers much.
/// </summary>
public class CodexVisualService : SingletonMonoBehaviour<CodexVisualService>
{
[GroupConfigurations, SerializeField] private PictorialProtocolTag defaultProtocol;
[GroupDebug, SerializeField] private PictorialProtocolTag currentProtocol;
private int version;
private PictorialProtocolTag committedProtocol;
private CodexVisualContext currentContext;
public PictorialProtocolTag CurrentPictorialProtocol => currentProtocol ?? defaultProtocol;
public CodexVisualContext CurrentContext
{
get
{
currentContext ??= new CodexVisualContext(CurrentPictorialProtocol, version);
return currentContext;
}
}
private void Awake()
{
SingletonInstantiation(false, _ => { });
committedProtocol = CurrentPictorialProtocol;
currentContext = new CodexVisualContext(CurrentPictorialProtocol, version);
}
public void ApplyTheme(PictorialProtocolTag protocol, CodexVisualChangeMode mode = CodexVisualChangeMode.Commit)
{
var nextProtocol = protocol ?? defaultProtocol;
var previousProtocol = CurrentPictorialProtocol;
if (previousProtocol == nextProtocol && mode != CodexVisualChangeMode.CancelPreview) return;
currentProtocol = nextProtocol;
if (CodexVisualChangeMode.Preview != mode)
{
committedProtocol = nextProtocol;
}
currentContext = new CodexVisualContext(CurrentPictorialProtocol, ++version, mode == CodexVisualChangeMode.Preview);
RaiseChanged(previousProtocol, CurrentPictorialProtocol, mode);
}
public void PreviewTheme(PictorialProtocolTag protocol)
{
ApplyTheme(protocol, CodexVisualChangeMode.Preview);
}
public void CancelPreview()
{
ApplyTheme(committedProtocol, CodexVisualChangeMode.CancelPreview);
}
private void RaiseChanged(PictorialProtocolTag previousProtocol, PictorialProtocolTag nextProtocol, CodexVisualChangeMode mode)
{
Proxy.Instance.EventBus.Raise(new CodexVisualChangedEvent
{
PreviousProtocol = previousProtocol,
CurrentProtocol = nextProtocol,
Context = CurrentContext,
Mode = mode
});
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a41317694c23246d8b43c7ffbe9205e5

View File

@ -0,0 +1,28 @@
namespace Intrepid.Gameplay.Codex.Visual
{
/// <summary>
/// Stable visual meanings. Concrete colors/materials come from the active pictorial protocol.
/// This starts small on purpose and can grow as PDA, ActionBelt and diegetic marks migrate.
/// </summary>
public enum CodexVisualToken
{
SurfacePrimary,
SurfaceSecondary,
SurfaceDeep,
TextPrimary,
TextMuted,
BorderSoft,
BorderStrong,
Focus,
Selected,
Disabled,
Denied,
Warning,
Danger,
Corruption,
Available,
Locked,
Unknown,
Reinterpreted
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: fa03f2923db1a44eba4d2349b7233ba1

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 25ababed467874e9a84f02aec86b7c9e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,13 @@
using Intrepid.Gameplay.Codex.Knowledge;
using Intrepid.SimpleEvents;
namespace Intrepid.Gameplay.Codex.Visual.Events
{
public class CodexVisualChangedEvent : IEventBase
{
public PictorialProtocolTag PreviousProtocol { get; set; }
public PictorialProtocolTag CurrentProtocol { get; set; }
public CodexVisualContext Context { get; set; }
public CodexVisualChangeMode Mode { get; set; }
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 375fdd9b497264f9cbf50e7fbe710524

View File

@ -0,0 +1,9 @@
using Intrepid.SimpleEvents;
namespace Intrepid.Gameplay.Codex.Visual.Events
{
public class PictorialProtocolUnlockedEvent : IEventBase
{
public PictorialProtocolTag Protocol { get; set; }
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7abf081f4dc7f451a8273b457e6e4067

View File

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Intrepid.Utilities;
using Sirenix.OdinInspector;
using UnityEngine;
namespace Intrepid.Gameplay.Codex.Visual
{
[Serializable]
[GroupConfigurations("Pictorial Protocol Inventory"), HideLabel]
public class PictorialProtocolInventory
{
[SerializeField] private List<PictorialProtocolTag> knownProtocols = new();
public IReadOnlyList<PictorialProtocolTag> KnownProtocols => knownProtocols.Where(p => p.IsNotNull()).Distinct().ToList();
public bool Knows(PictorialProtocolTag protocol)
{
return protocol.IsNotNull() && knownProtocols.Contains(protocol);
}
public bool Unlock(PictorialProtocolTag protocol)
{
if (protocol.IsNull()) return false;
if (knownProtocols.Contains(protocol)) return false;
knownProtocols.Add(protocol);
return true;
}
public bool Forget(PictorialProtocolTag protocol)
{
if (protocol.IsNull()) return false;
return knownProtocols.Remove(protocol);
}
#if UNITY_EDITOR
[GroupDebug, Button(ButtonSizes.Medium)]
private void CleanupKnownProtocols()
{
knownProtocols = knownProtocols.Where(p => p.IsNotNull()).Distinct().ToList();
}
#endif
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a488e1dac0ec4c01929a364477d39f27

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Intrepid.Utilities;
using Sirenix.OdinInspector;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace Intrepid.Gameplay.Codex.Visual
{
/// <summary>
/// Catalog of all pictorial protocols available in the game.
/// Currently uses the existing Theme asset as the concrete protocol asset.
/// </summary>
[Serializable]
[CreateAssetMenu(fileName = "Codex - Pictorial Protocol Library", menuName = "GON/CODEX/Visual/Pictorial Protocol Library")]
public class PictorialProtocolLibrary : SingletonScriptableObject<PictorialProtocolLibrary>
{
[GroupInjections, SerializeField, ReadOnly] private List<PictorialProtocolTag> protocols;
public IReadOnlyList<PictorialProtocolTag> Protocols => protocols.Where(p => p.IsNotNull()).Distinct().ToList();
public bool Contains(PictorialProtocolTag protocol)
{
return protocol is not null && protocols.Contains(protocol);
}
#if UNITY_EDITOR
[PropertyOrder(-10)]
[GroupInjections, Button(ButtonSizes.Large), GUIColor(0.35f, 0.8f, .35f)]
protected void FindAll()
{
protocols.Clear();
var guids = AssetDatabase.FindAssets($"t:{nameof(PictorialProtocolTag)}");
foreach (var guid in guids)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
var protocol = AssetDatabase.LoadAssetAtPath<PictorialProtocolTag>(path);
protocols.Add(protocol);
}
EditorUtility.SetDirty(this);
}
#endif
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 73c8da23e167c437b9b4044376377a24

View File

@ -0,0 +1,178 @@
using System;
using Intrepid.Diagnostics;
using Intrepid.Localization;
using Intrepid.TagSystem;
using Intrepid.Utilities;
using Sirenix.OdinInspector;
using UnityEngine;
namespace Intrepid.Gameplay.Codex.Visual
{
[Serializable]
[CreateAssetMenu(fileName = "New Pictorial Protocol Tag", menuName = "GON/CODEX/Visual/Pictorial Protocol Tag")]
public class PictorialProtocolTag : Tag
{
[GroupInjections, SerializeField] private LocalizationTag localization;
[GroupInjections, SerializeField] private LocalizationTag description;
[SerializeField, HideLabel, Title("Interface Style")] private PictorialProtocolInterfaceStyle interfaceStyle;
public LocalizationTag Localization => localization;
public LocalizationTag Description => description;
public PictorialProtocolInterfaceStyle InterfaceStyle => interfaceStyle;
// katakana
// kanji
}
[Serializable]
public class PictorialProtocolInterfaceStyle
{
[GroupConfigurations("Surfaces"), SerializeField] private Color surfaceBase = Color.black;
[GroupConfigurations("Surfaces"), SerializeField] private Color surfaceRaised = Color.black;
[GroupConfigurations("Surfaces"), SerializeField] private Color surfaceDeep = Color.black;
[GroupConfigurations("Surfaces"), SerializeField] private Color surfaceOverlay = Color.black;
[GroupConfigurations("Text"), SerializeField] private Color textPrimary = Color.white;
[GroupConfigurations("Text"), SerializeField] private Color textSecondary = Color.gray;
[GroupConfigurations("Text"), SerializeField] private Color textMuted = Color.gray;
[GroupConfigurations("Text"), SerializeField] private Color textDisabled = Color.gray;
[GroupConfigurations("Signals"), SerializeField] private Color focus = Color.white;
[GroupConfigurations("Signals"), SerializeField] private Color selected = Color.white;
[GroupConfigurations("Signals"), SerializeField] private Color disabled = Color.gray;
[GroupConfigurations("Signals"), SerializeField] private Color denied = Color.red;
[GroupConfigurations("Signals"), SerializeField] private Color warning = Color.yellow;
[GroupConfigurations("Signals"), SerializeField] private Color danger = Color.red;
[GroupConfigurations("Signals"), SerializeField] private Color corruption = Color.magenta;
[GroupConfigurations("Resources"), SerializeField] private Color life = Color.red;
[GroupConfigurations("Resources"), SerializeField] private Color shield = Color.cyan;
[GroupConfigurations("Resources"), SerializeField] private Color cadence = Color.white;
[GroupConfigurations("Resources"), SerializeField] private Color progress = Color.white;
[GroupConfigurations("Lines"), SerializeField] private Color lineSubtle = Color.gray;
[GroupConfigurations("Lines"), SerializeField] private Color lineActive = Color.white;
[GroupConfigurations("Lines"), SerializeField] private Color lineCritical = Color.red;
public Color SurfaceBase => surfaceBase;
public Color SurfaceRaised => surfaceRaised;
public Color SurfaceDeep => surfaceDeep;
public Color SurfaceOverlay => surfaceOverlay;
public Color TextPrimary => textPrimary;
public Color TextSecondary => textSecondary;
public Color TextMuted => textMuted;
public Color TextDisabled => textDisabled;
public Color Focus => focus;
public Color Selected => selected;
public Color Disabled => disabled;
public Color Denied => denied;
public Color Warning => warning;
public Color Danger => danger;
public Color Corruption => corruption;
public Color Life => life;
public Color Shield => shield;
public Color Cadence => cadence;
public Color Progress => progress;
public Color LineSubtle => lineSubtle;
public Color LineActive => lineActive;
public Color LineCritical => lineCritical;
private bool TryParseColor(string colorSchema, int nColors, out Color[] colors)
{
colors = null;
if (string.IsNullOrWhiteSpace(colorSchema))
{
DLogger.LogWarn("Color Schema is empty.");
return false;
}
var colorStrings = colorSchema.Split(',');
if (colorStrings.Length < nColors)
{
DLogger.LogWarn($"Palette string must contain at least {nColors} hex colors.");
return false;
}
colors = new Color[colorStrings.Length];
for (var i = 0; i < colorStrings.Length; i++)
{
if (ColorUtility.TryParseHtmlString(colorStrings[i].Trim(), out var color))
{
colors[i] = color;
}
else
{
DLogger.LogWarn($"Invalid color code: {colorStrings[i]}");
return false;
}
}
return true;
}
[GroupDebug, Button(ButtonSizes.Medium, ButtonStyle.FoldoutButton)]
private void SetSurfaceColorSchema(string colorSchema)
{
if (TryParseColor(colorSchema, 4, out var colors))
{
surfaceBase = colors[0];
surfaceRaised = colors[1];
surfaceDeep = colors[2];
surfaceOverlay = colors[3];
}
}
[GroupDebug, Button(ButtonSizes.Medium, ButtonStyle.FoldoutButton)]
private void SetTextColorSchema(string colorSchema)
{
if (TryParseColor(colorSchema, 4, out var colors))
{
textPrimary = colors[0];
textSecondary = colors[1];
textMuted = colors[2];
textDisabled = colors[3];
}
}
[GroupDebug, Button(ButtonSizes.Medium, ButtonStyle.FoldoutButton)]
private void SetSignalsColorSchema(string colorSchema)
{
if (TryParseColor(colorSchema, 7, out var colors))
{
focus = colors[0];
selected = colors[1];
disabled = colors[2];
denied = colors[3];
warning = colors[4];
danger = colors[5];
corruption = colors[6];
}
}
[GroupDebug, Button(ButtonSizes.Medium, ButtonStyle.FoldoutButton)]
private void SetResourceColorSchema(string colorSchema)
{
if (TryParseColor(colorSchema, 4, out var colors))
{
life = colors[0];
shield = colors[1];
cadence = colors[2];
progress = colors[3];
}
}
[GroupDebug, Button(ButtonSizes.Medium, ButtonStyle.FoldoutButton)]
private void SetLineColorSchema(string colorSchema)
{
if (TryParseColor(colorSchema, 3, out var colors))
{
lineSubtle = colors[0];
lineActive = colors[1];
lineCritical = colors[2];
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b9a2dd3bc25546bfb126b9445e977c3c
timeCreated: 1783113533