Port: DeltaV RealTraits#179
Open
rebaserHEAD wants to merge 59 commits into
Open
Conversation
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
* 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
…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.
Drop the two integration test fixtures this PR added (TraitSystemTest, SericultureRetriggerTest). No other code references them.
Collaborator
|
Stop giving me more delicious garbage, I haven't even finish the previous ones |
|
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
Requirements
How to test
Breaking changes
traitDB table is truncated on first boot after deploy (PostgresTRUNCATE, SQLiteDELETE); every player's saved trait selections are cleared and the migration is one-way (noDown()). Take a DB backup before applying to production. This is intentional: the rework renames every trait id, so old selections no longer resolve.TraitPrototypeno longer usesblacklist/whitelist; eligibility is expressed with condition objects (!type:IsSpeciesCondition,HasCompCondition,HasJobCondition,InDepartmentCondition,HasTraitCondition,AnyOfCondition, etc.) and effects (AddCompsEffect,OverrideCompsEffect,RemCompsEffect,RemoveBodyPartEffect,SpawnItemInHandEffect).categoryis now a required field andTraitCategoryPrototype.MaxPointsis nullable (null = unlimited).DCCVars.MaxTraitCount/DCCVars.MaxTraitPointsCVars for the global caps.Changelog
🆑