Clarify and streamline D&D item events
This commit is contained in:
@@ -7,8 +7,11 @@ Use `discovered` when the party learns of or encounters an item without
|
||||
establishing possession. Use `acquired` when the party or a party member gains
|
||||
possession. Use `lost` when party possession ends through a gift, sale, payment,
|
||||
theft, abandonment, or destruction not caused by intended use. Use `consumed`
|
||||
when intended use depletes an item or currency. Use `transferred` only when
|
||||
possession moves between two distinct named party members.
|
||||
when intended use depletes an expendable item. Monetary spending, purchases, and
|
||||
payments are always `lost`, not `consumed`. Classify currency as `consumed` only
|
||||
when the transcript explicitly describes it being physically destroyed or
|
||||
expended as a non-payment component. Use `transferred` only when possession
|
||||
moves between two distinct named party members.
|
||||
|
||||
For `discovered`, omit both holders. For `acquired`, provide only `to`; for
|
||||
`lost` and `consumed`, provide only `from`; and for `transferred`, provide both
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/itemevents"
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared/diagnostics"
|
||||
itemeventshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemevents/shape"
|
||||
)
|
||||
@@ -43,19 +42,18 @@ func (v *Validator) Validate(_ context.Context, req contracts.TypedValidationReq
|
||||
return contracts.ValidationResult{Approved: true}, nil
|
||||
}
|
||||
index := source.NewDocumentIndex(req.Source)
|
||||
var coverage *chunkCoverage
|
||||
if req.Stage == string(pipeline.StageExtract) {
|
||||
coverage = newChunkCoverage(req.Chunk)
|
||||
}
|
||||
issues := make([]string, 0)
|
||||
for eventIndex, event := range req.Value.Events {
|
||||
if itemevents.ValidSourceRefs(index, event.SourceRefs) {
|
||||
if req.Stage != string(pipeline.StageExtract) || refsFitChunk(req.Source, req.Chunk, event.SourceRefs) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
for refIndex, ref := range event.SourceRefs {
|
||||
if err := index.ValidateRef(ref); err != nil {
|
||||
issues = append(issues, fmt.Sprintf("events[%d].source_refs[%d]: %s", eventIndex, refIndex, diagnostics.Truncate(err.Error())))
|
||||
continue
|
||||
}
|
||||
if req.Stage == string(pipeline.StageExtract) && !chunkContainsRef(req.Source, req.Chunk, ref) {
|
||||
if coverage != nil && !coverage.contains(req.Source, ref) {
|
||||
issues = append(issues, fmt.Sprintf("events[%d].source_refs[%d]: source reference is outside the current extraction chunk", eventIndex, refIndex))
|
||||
}
|
||||
}
|
||||
@@ -66,17 +64,24 @@ func (v *Validator) Validate(_ context.Context, req contracts.TypedValidationReq
|
||||
return contracts.ValidationResult{Approved: false, ReasonCode: ReasonCode, Message: diagnostics.Aggregate("invalid item event source references", issues)}, nil
|
||||
}
|
||||
|
||||
func refsFitChunk(doc *source.SourceDocument, chunk *source.Chunk, refs []source.SourceRef) bool {
|
||||
for _, ref := range refs {
|
||||
if !chunkContainsRef(doc, chunk, ref) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
type chunkCoverage struct {
|
||||
sourceID string
|
||||
unitIDs map[int]struct{}
|
||||
}
|
||||
|
||||
func chunkContainsRef(doc *source.SourceDocument, chunk *source.Chunk, ref source.SourceRef) bool {
|
||||
if chunk == nil || ref.SourceID != chunk.SourceID {
|
||||
func newChunkCoverage(chunk *source.Chunk) *chunkCoverage {
|
||||
coverage := &chunkCoverage{
|
||||
sourceID: chunk.SourceID,
|
||||
unitIDs: make(map[int]struct{}, len(chunk.Units)),
|
||||
}
|
||||
for _, unit := range chunk.Units {
|
||||
coverage.unitIDs[unit.ID] = struct{}{}
|
||||
}
|
||||
return coverage
|
||||
}
|
||||
|
||||
func (coverage *chunkCoverage) contains(doc *source.SourceDocument, ref source.SourceRef) bool {
|
||||
if coverage == nil || doc == nil || ref.SourceID != coverage.sourceID {
|
||||
return false
|
||||
}
|
||||
start, startOK := source.UnitIndex(doc, ref.StartUnitID)
|
||||
@@ -84,12 +89,8 @@ func chunkContainsRef(doc *source.SourceDocument, chunk *source.Chunk, ref sourc
|
||||
if !startOK || !endOK || start > end {
|
||||
return false
|
||||
}
|
||||
units := make(map[int]struct{}, len(chunk.Units))
|
||||
for _, unit := range chunk.Units {
|
||||
units[unit.ID] = struct{}{}
|
||||
}
|
||||
for position := start; position <= end; position++ {
|
||||
if _, found := units[doc.Units[position].ID]; !found {
|
||||
if _, found := coverage.unitIDs[doc.Units[position].ID]; !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user