Skip to content

Port: DeltaV RealTraits#179

Open
rebaserHEAD wants to merge 59 commits into
Triad-Sector:mainfrom
rebaserHEAD:feat/real-traits-port
Open

Port: DeltaV RealTraits#179
rebaserHEAD wants to merge 59 commits into
Triad-Sector:mainfrom
rebaserHEAD:feat/real-traits-port

Conversation

@rebaserHEAD

@rebaserHEAD rebaserHEAD commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

About the PR

Ports DeltaV's Real Traits system (DDrakov PR #152 / DeltaV #5598) into Triad: character traits move from a flat list to a category-based selector with a points budget, where each trait is gated by data-driven conditions (species, job, department, component, other traits) and applies data-driven effects (add/override/remove components, remove body parts, spawn items). This PR also includes the hardening pass on top of the port: deterministic validation, crash-safety, the silicon guards the conversion dropped, and trait-menu UX fixes.

Why / Balance

Traits are now organized into categories (Physical, Medical, Mental, Disabilities, Accents, Languages), each with its own trait-count and point caps, layered under a global trait-point budget; negative-cost traits grant points to spend on positives. Validation now resolves the same way on the client preview, on profile save, and at spawn, so what you pick in the lobby is what you get. Silicon/borg chassis are once again blocked from biology-coupled traits that the blacklist-to-condition conversion had silently let through.

Media

Done

Requirements

  • I have read relevant guidelines/documentation to this PR found on our devwiki.
  • I have added media to this PR or it does not require an ingame showcase.
  • I can confirm this PR contains either no AI-generated content, or AI-generated content that meets our guidelines.

How to test

  1. Open the character editor and go to the Traits tab. Categories start collapsed with their per-category caps visible side by side; the search box expands categories that have matches.
  2. Select traits and confirm the per-category trait-count/point caps and the global point budget are enforced, and that negative-cost traits free up points.
  3. Pick condition-gated traits on eligible and ineligible characters (e.g. species-locked, job-locked, component-locked) and confirm they enable/disable correctly and that any over-cap or unmet picks are reported rather than silently applied.
  4. Spawn and confirm the selected traits apply (components added, body parts removed, items spawned) and that ineligible traits are dropped without crashing the spawn.
  5. Confirm long trait descriptions wrap to the column instead of clipping.

Breaking changes

  • The trait DB table is truncated on first boot after deploy (Postgres TRUNCATE, SQLite DELETE); every player's saved trait selections are cleared and the migration is one-way (no Down()). Take a DB backup before applying to production. This is intentional: the rework renames every trait id, so old selections no longer resolve.
  • TraitPrototype no longer uses blacklist/whitelist; eligibility is expressed with condition objects (!type:IsSpeciesCondition, HasCompCondition, HasJobCondition, InDepartmentCondition, HasTraitCondition, AnyOfCondition, etc.) and effects (AddCompsEffect, OverrideCompsEffect, RemCompsEffect, RemoveBodyPartEffect, SpawnItemInHandEffect). category is now a required field and TraitCategoryPrototype.MaxPoints is nullable (null = unlimited).
  • Adds the DCCVars.MaxTraitCount / DCCVars.MaxTraitPoints CVars for the global caps.

Changelog
🆑

  • add: Character traits are now grouped into categories (Physical, Medical, Mental, Disabilities, Accents, Languages), each with its own limits, on top of an overall trait-point budget. Negative-cost traits grant extra points to spend.
  • tweak: The Traits menu now opens with categories collapsed and supports searching.
  • tweak: Existing trait selections have been reset as part of the trait rework; please reselect your traits.

Toby222 and others added 30 commits January 29, 2026 23:00
* why

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

(cherry picked from commit 96304b787bc69dc51ceffa191ff770554888dbd6)
HasTraitCondition instead of Conflicts and Requirements
RemoveBodyPartEffect instead of AmputeeComponent/System
Remove redundant BorgChassis checks
Remove failed condition check and just show the full one
Improve vision trait conditions
Erol509 and others added 14 commits May 11, 2026 14:32
…its system

Adopts DDrakov's Triad-side port of the DeltaV/Monolith Real Traits
system (Monolith #3153/#3434 lineage) onto current main. Replaces the
flat trait schema (components + speciesBlacklist) with composable
conditions + effects and a categorized trait selection UI.

Port only. The Sericulture interrupt fix and trait-table migration
re-dating land in follow-up commits.

Source: https://github.com/DDrakov/Triad_Sector/tree/real-traits
DDrakov's branch imported Monolith's TruncateTraitTable migration
verbatim (Postgres 20260117145027 / Sqlite 20260117145007), which sorts
*before* Triad's existing AdminRankShortTitle (0121) and ConsentV2 (0516)
migrations and carried Monolith's model snapshot in its Designer.

Regenerate it fresh against Triad's current chain so it lands last
(20260601), with the correct Triad snapshot, for both Postgres and
Sqlite. Migration is data-only (clears the now-incompatible trait rows),
so model snapshots are unchanged.
The Sericulture weave DoAfter set both BlockDuplicate and CancelDuplicate.
The weave action re-lights after its 1s useDelay, a full second before the
2s production DoAfter finishes, so an impatient re-click hit TryStartDoAfter
again: CancelDuplicate killed the in-flight weave while BlockDuplicate
started nothing in its place. Net result was the Bionic Spinarette trait
(and native arachnid weaving) getting interrupted every time and producing
no silk.

Set CancelDuplicate = false so a re-trigger is a harmless no-op (the existing
weave keeps running, BlockDuplicate still prevents stacking). Add a
regression test that fails on the old behaviour and passes on the new.
The Real Traits migration converted BionicSpinarette's species blacklist to
IsSpeciesCondition entries but dropped the old 'blacklist: components:
[BorgChassis]'. Re-add it as an inverted HasCompCondition so borg chassis
entities still can't take the trait, matching pre-port behaviour.
Apply selected traits ordered by Priority (desc) then Cost (asc) instead
of in arbitrary validation order, so order-sensitive effects (add-vs-remove
of the same component across two traits) resolve deterministically. Adds
the Priority field to TraitPrototype to drive the ordering.

Ported from DeltaV #5598 (originally Floof/Panta-Rhei Triad-Sector#75/#347). The balance
tweaks bundled in that PR (trait max 10->25, default cost 1->0) are left out
intentionally.
The Real Traits merge incidentally flipped four consent timestamp writes in
ServerDbBase (ConsentFreetextUpdatedAt x2, ReadAt x2) from DateTime.UtcNow
to DateTime.Now, storing server-local time while the rest of the file and
the schema use UTC. Restore UtcNow so timestamps stay comparable on
non-UTC hosts.
ValidateTraits iterated the selected-trait set in hash order, so when a
profile exceeded a global or category cap, *which* traits got cut was
non-deterministic: two identical profiles could keep different subsets, and
the same profile could vary between spawns. Sort selections by Priority
desc, then Cost asc, then id before enforcing limits, so the surviving
subset is stable and matches the application order.
The Real Traits migration uniquely zeroed ScottishAccent's cost (1 -> 0) and
dropped its granted bagpipe, while sibling accents kept their cost. Restore
cost 1 and re-grant BagpipeInstrument via SpawnItemInHandEffect, matching the
pre-port traitGear behaviour. Revert if the 0-cost normalization was intended.
…s port

The port converted the old component blacklist to conditions but only carried the BorgChassis exclusion onto 4 of the 17 traits that had it. Restore it on the remaining 13 (10 in physical.yml plus Redshirt, Muted, and WheelchairBound) as a HasCompCondition, so a silicon/borg chassis can't take biology-coupled traits.
- GetValidTraits prunes in a stable (priority, cost, id) order instead of HashSet iteration order, so a profile over a category cap keeps the same traits across restarts.
- AnyOfCondition no longer throws on Invert (the base class already XORs it), and the server CheckConditions wraps evaluation in try/catch so a malformed condition rejects its trait instead of crashing spawn.
- ValidateTraits returns an already-ordered list, removing the duplicate apply-time sort and the repeated prototype lookups in the comparator; an ordinal id tiebreaker keeps ordering host-independent.
- RemoveBodyPartEffect materializes the body iterators before deleting, drops the double-delete of the part, and corrects the bleed comment to match the code.
- Drop the dead MaxPoints < 0 unlimited sentinel; HasCompCondition uses TryGetRegistration instead of exception-as-control-flow.
- Update TraitSystemTest for the ValidateTraits return type.
- Trait categories default to collapsed so the per-category headers and point caps sit adjacent and read at a glance. Search force-expands categories with matches and restores the collapsed state when cleared.
- Trait descriptions now wrap to the column. This editor measures tab content wider than it finally arranges, so the description's max width is pinned to the entry's actual arranged width, the same workaround the global points bar already uses.
@rebaserHEAD rebaserHEAD changed the title Feat/real traits port Port: DeltaV RealTraits Jun 2, 2026
Comment thread Content.IntegrationTests/Tests/_Triad/SericultureRetriggerTest.cs Outdated
Drop the two integration test fixtures this PR added (TraitSystemTest, SericultureRetriggerTest). No other code references them.
@DDrakov

DDrakov commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Stop giving me more delicious garbage, I haven't even finish the previous ones

@github-actions

Copy link
Copy Markdown

This pull request has conflicts, please resolve those before we can evaluate the pull request.

# Conflicts:
#	Content.Server/Database/ServerDbBase.cs
…ctor

Merging main brought in BanRefactor (20260606), which sorted after the
trait truncation migration (20260601). Regenerated the migration on both
contexts at 20260612 so it sits last on the chain with a Designer that
matches the post-BanRefactor model. Up()/Down() bodies are unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants