323 lines
20 KiB
Transact-SQL
323 lines
20 KiB
Transact-SQL
SET ANSI_NULLS ON;
|
|
GO
|
|
SET QUOTED_IDENTIFIER ON;
|
|
GO
|
|
|
|
MERGE dbo.WarningTypes AS target
|
|
USING (VALUES
|
|
(N'Knowledge Continuity', N'Character may act on information before a recorded knowledge event.', 220),
|
|
(N'Abrupt Relationship Change', N'Relationship intensity changes sharply between nearby scenes.', 230),
|
|
(N'Long Character Absence', N'Character disappears for a long stretch of narrative scenes.', 240),
|
|
(N'Character Underused', N'Character appears lightly compared with their story connections.', 250),
|
|
(N'Scene Character Overload', N'Scene contains a large number of character appearances.', 260)
|
|
) AS source (TypeName, Description, SortOrder)
|
|
ON target.TypeName = source.TypeName
|
|
WHEN MATCHED THEN UPDATE SET Description = source.Description, SortOrder = source.SortOrder, IsActive = 1
|
|
WHEN NOT MATCHED THEN INSERT (TypeName, Description, SortOrder, IsActive) VALUES (source.TypeName, source.Description, source.SortOrder, 1);
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.Dynamics_CharacterArc
|
|
@CharacterID int
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
|
|
DECLARE @ProjectID int = (SELECT ProjectID FROM dbo.Characters WHERE CharacterID = @CharacterID);
|
|
|
|
SELECT CharacterID, ProjectID, CharacterName, ShortName, Sex, BirthDate, AgeAtSeriesStart, AgeReferenceSceneID,
|
|
Height, EyeColour, DefaultDescription, CreatedDate, UpdatedDate, IsArchived
|
|
FROM dbo.Characters
|
|
WHERE CharacterID = @CharacterID;
|
|
|
|
SELECT sc.SceneID, nso.BookTitle, nso.ChapterNumber, nso.SceneNumber, nso.SceneTitle, nso.GlobalSceneIndex,
|
|
CAST(CASE WHEN COALESCE(s.POVCharacterID, c.POVCharacterID) = @CharacterID THEN 1 ELSE 0 END AS bit) AS IsPov,
|
|
MAX(CASE WHEN smt.MetricName = N'Emotional Weight' THEN smv.Value END) AS EmotionalWeight,
|
|
MAX(CASE WHEN smt.MetricName = N'Tension' THEN smv.Value END) AS Tension,
|
|
MAX(CASE WHEN smt.MetricName = N'Romance' THEN smv.Value END) AS Romance,
|
|
MAX(CASE WHEN smt.MetricName = N'Action' THEN smv.Value END) AS Action,
|
|
MAX(CASE WHEN smt.MetricName = N'Darkness' THEN smv.Value END) AS Darkness,
|
|
MAX(CASE WHEN smt.MetricName = N'Hope / Lightness' THEN smv.Value END) AS HopeLightness,
|
|
MAX(CASE WHEN smt.MetricName = N'Overall Intensity' THEN smv.Value END) AS OverallIntensity,
|
|
role.TypeName AS RoleInSceneTypeName,
|
|
presence.TypeName AS PresenceTypeName,
|
|
sc.EmotionalState,
|
|
sc.AppearanceNotes,
|
|
notes.KeyNotes
|
|
FROM dbo.SceneCharacters sc
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = sc.SceneID
|
|
INNER JOIN dbo.Scenes s ON s.SceneID = sc.SceneID
|
|
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
|
|
LEFT JOIN dbo.CharacterRoleInSceneTypes role ON role.CharacterRoleInSceneTypeID = sc.RoleInSceneTypeID
|
|
LEFT JOIN dbo.PresenceTypes presence ON presence.PresenceTypeID = sc.PresenceTypeID
|
|
LEFT JOIN dbo.SceneMetricValues smv ON smv.SceneID = sc.SceneID
|
|
LEFT JOIN dbo.SceneMetricTypes smt ON smt.MetricTypeID = smv.MetricTypeID
|
|
OUTER APPLY
|
|
(
|
|
SELECT STRING_AGG(CONVERT(nvarchar(max), COALESCE(sn.NoteTitle, sn.NoteText)), N' | ') AS KeyNotes
|
|
FROM dbo.SceneNotes sn
|
|
INNER JOIN dbo.SceneNoteTypes snt ON snt.SceneNoteTypeID = sn.SceneNoteTypeID
|
|
WHERE sn.SceneID = sc.SceneID AND sn.IsResolved = 0
|
|
AND snt.TypeName IN (N'Emotional Intent', N'Character Reminder', N'Revision Note', N'TODO')
|
|
) notes
|
|
WHERE sc.CharacterID = @CharacterID
|
|
GROUP BY sc.SceneID, nso.BookTitle, nso.ChapterNumber, nso.SceneNumber, nso.SceneTitle, nso.GlobalSceneIndex,
|
|
s.POVCharacterID, c.POVCharacterID, role.TypeName, presence.TypeName, sc.EmotionalState, sc.AppearanceNotes, notes.KeyNotes
|
|
ORDER BY nso.GlobalSceneIndex;
|
|
|
|
EXEC dbo.RelationshipEvent_ListByCharacter @CharacterID;
|
|
EXEC dbo.CharacterKnowledge_ListByCharacter @CharacterID;
|
|
|
|
SELECT N'Character Presence' AS Category, ws.SeverityName, cw.Message AS Title, cw.Details AS Detail, cw.SceneID, cw.EntityID, cw.EntityType
|
|
FROM dbo.ContinuityWarnings cw
|
|
INNER JOIN dbo.WarningSeverities ws ON ws.WarningSeverityID = cw.WarningSeverityID
|
|
INNER JOIN dbo.WarningTypes wt ON wt.WarningTypeID = cw.WarningTypeID
|
|
WHERE cw.ProjectID = @ProjectID AND cw.IsDismissed = 0
|
|
AND (
|
|
(cw.EntityType = N'Character' AND cw.EntityID = @CharacterID)
|
|
OR cw.SceneID IN (SELECT SceneID FROM dbo.SceneCharacters WHERE CharacterID = @CharacterID)
|
|
)
|
|
AND wt.TypeName IN (N'Knowledge Continuity', N'Long Character Absence', N'Character Underused', N'Scene Character Overload');
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.Dynamics_RelationshipEvolution
|
|
@CharacterRelationshipID int
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
|
|
SELECT cr.CharacterRelationshipID, cr.ProjectID, cr.CharacterAID, ca.CharacterName AS CharacterAName,
|
|
cr.CharacterBID, cb.CharacterName AS CharacterBName, cr.RelationshipTypeID, rt.TypeName AS RelationshipTypeName,
|
|
cr.IsPermanent, cr.StartSceneID, cr.EndSceneID, cr.IsKnownToReader, cr.Notes, cr.CreatedDate, cr.UpdatedDate, cr.IsArchived
|
|
FROM dbo.CharacterRelationships cr
|
|
INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID
|
|
INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID
|
|
INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID
|
|
WHERE cr.CharacterRelationshipID = @CharacterRelationshipID;
|
|
|
|
SELECT re.RelationshipEventID, re.CharacterRelationshipID, re.SceneID, nso.BookTitle, nso.ChapterNumber, nso.SceneNumber,
|
|
nso.SceneTitle, nso.GlobalSceneIndex, rs.StateName AS RelationshipStateName, re.Intensity, re.IsKnownToOtherCharacter, re.Description
|
|
FROM dbo.RelationshipEvents re
|
|
INNER JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = re.RelationshipStateID
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = re.SceneID
|
|
WHERE re.CharacterRelationshipID = @CharacterRelationshipID
|
|
ORDER BY nso.GlobalSceneIndex, re.RelationshipEventID;
|
|
|
|
SELECT N'Relationship Continuity' AS Category, ws.SeverityName, cw.Message AS Title, cw.Details AS Detail, cw.SceneID, cw.EntityID, cw.EntityType
|
|
FROM dbo.ContinuityWarnings cw
|
|
INNER JOIN dbo.WarningSeverities ws ON ws.WarningSeverityID = cw.WarningSeverityID
|
|
INNER JOIN dbo.WarningTypes wt ON wt.WarningTypeID = cw.WarningTypeID
|
|
WHERE cw.IsDismissed = 0 AND cw.EntityType = N'Relationship' AND cw.EntityID = @CharacterRelationshipID
|
|
AND wt.TypeName = N'Abrupt Relationship Change';
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.Dynamics_KnowledgeTimeline
|
|
@CharacterID int
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
DECLARE @ProjectID int = (SELECT ProjectID FROM dbo.Characters WHERE CharacterID = @CharacterID);
|
|
|
|
SELECT CharacterID, ProjectID, CharacterName, ShortName, Sex, BirthDate, AgeAtSeriesStart, AgeReferenceSceneID,
|
|
Height, EyeColour, DefaultDescription, CreatedDate, UpdatedDate, IsArchived
|
|
FROM dbo.Characters
|
|
WHERE CharacterID = @CharacterID;
|
|
|
|
EXEC dbo.CharacterKnowledge_ListByCharacter @CharacterID;
|
|
|
|
SELECT N'Knowledge Continuity' AS Category, ws.SeverityName, cw.Message AS Title, cw.Details AS Detail, cw.SceneID, cw.EntityID, cw.EntityType
|
|
FROM dbo.ContinuityWarnings cw
|
|
INNER JOIN dbo.WarningSeverities ws ON ws.WarningSeverityID = cw.WarningSeverityID
|
|
INNER JOIN dbo.WarningTypes wt ON wt.WarningTypeID = cw.WarningTypeID
|
|
WHERE cw.ProjectID = @ProjectID AND cw.IsDismissed = 0
|
|
AND cw.EntityType = N'Character' AND cw.EntityID = @CharacterID
|
|
AND wt.TypeName = N'Knowledge Continuity';
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.Dynamics_StoryDashboard
|
|
@ProjectID int
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
|
|
;WITH Pairings AS
|
|
(
|
|
SELECT a.CharacterID AS CharacterAID, ca.CharacterName AS CharacterAName,
|
|
b.CharacterID AS CharacterBID, cb.CharacterName AS CharacterBName,
|
|
a.SceneID, nso.GlobalSceneIndex,
|
|
CONCAT(nso.BookTitle, N' / Ch ', nso.ChapterNumber, N' / Scene ', nso.SceneNumber, N': ', nso.SceneTitle) AS SceneLabel
|
|
FROM dbo.SceneCharacters a
|
|
INNER JOIN dbo.SceneCharacters b ON b.SceneID = a.SceneID AND b.CharacterID > a.CharacterID
|
|
INNER JOIN dbo.Characters ca ON ca.CharacterID = a.CharacterID
|
|
INNER JOIN dbo.Characters cb ON cb.CharacterID = b.CharacterID
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = a.SceneID
|
|
WHERE ca.ProjectID = @ProjectID AND cb.ProjectID = @ProjectID AND ca.IsArchived = 0 AND cb.IsArchived = 0
|
|
),
|
|
Ranked AS
|
|
(
|
|
SELECT CharacterAID, CharacterAName, CharacterBID, CharacterBName, COUNT(*) AS SharedSceneCount,
|
|
MIN(GlobalSceneIndex) AS FirstIndex, MAX(GlobalSceneIndex) AS LastIndex
|
|
FROM Pairings
|
|
GROUP BY CharacterAID, CharacterAName, CharacterBID, CharacterBName
|
|
)
|
|
SELECT TOP (40) r.CharacterAID, r.CharacterAName, r.CharacterBID, r.CharacterBName, r.SharedSceneCount,
|
|
firstScene.SceneLabel AS FirstSceneLabel,
|
|
lastScene.SceneLabel AS LastSceneLabel
|
|
FROM Ranked r
|
|
OUTER APPLY (SELECT TOP (1) SceneLabel FROM Pairings p WHERE p.CharacterAID = r.CharacterAID AND p.CharacterBID = r.CharacterBID ORDER BY p.GlobalSceneIndex) firstScene
|
|
OUTER APPLY (SELECT TOP (1) SceneLabel FROM Pairings p WHERE p.CharacterAID = r.CharacterAID AND p.CharacterBID = r.CharacterBID ORDER BY p.GlobalSceneIndex DESC) lastScene
|
|
ORDER BY r.SharedSceneCount DESC, r.CharacterAName, r.CharacterBName;
|
|
|
|
SELECT Category, SeverityName, Title, Detail, SceneID, EntityID, EntityType
|
|
FROM
|
|
(
|
|
SELECT N'Continuity' AS Category, ws.SeverityName, cw.Message AS Title, cw.Details AS Detail, cw.SceneID, cw.EntityID, cw.EntityType
|
|
FROM dbo.ContinuityWarnings cw
|
|
INNER JOIN dbo.WarningSeverities ws ON ws.WarningSeverityID = cw.WarningSeverityID
|
|
INNER JOIN dbo.WarningTypes wt ON wt.WarningTypeID = cw.WarningTypeID
|
|
WHERE cw.ProjectID = @ProjectID AND cw.IsDismissed = 0
|
|
AND wt.TypeName IN (N'Knowledge Continuity', N'Abrupt Relationship Change', N'Long Character Absence', N'Character Underused', N'Scene Character Overload')
|
|
) issues
|
|
ORDER BY SeverityName DESC, Title;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.Dynamics_RunContinuityChecks
|
|
@ProjectID int,
|
|
@BookID int = NULL,
|
|
@SceneID int = NULL
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
|
|
;WITH RelationshipSteps AS
|
|
(
|
|
SELECT cr.CharacterRelationshipID, cr.ProjectID, nso.BookID, nso.ChapterID, re.SceneID,
|
|
ca.CharacterName AS CharacterAName, cb.CharacterName AS CharacterBName, re.Intensity,
|
|
LAG(re.Intensity) OVER (PARTITION BY cr.CharacterRelationshipID ORDER BY nso.GlobalSceneIndex, re.RelationshipEventID) AS PreviousIntensity,
|
|
LAG(nso.SceneTitle) OVER (PARTITION BY cr.CharacterRelationshipID ORDER BY nso.GlobalSceneIndex, re.RelationshipEventID) AS PreviousSceneTitle
|
|
FROM dbo.RelationshipEvents re
|
|
INNER JOIN dbo.CharacterRelationships cr ON cr.CharacterRelationshipID = re.CharacterRelationshipID AND cr.IsArchived = 0
|
|
INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID
|
|
INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = re.SceneID
|
|
WHERE cr.ProjectID = @ProjectID
|
|
)
|
|
INSERT dbo.ContinuityWarnings (ProjectID, BookID, ChapterID, SceneID, EntityType, EntityID, WarningTypeID, WarningSeverityID, Message, Details)
|
|
SELECT rs.ProjectID, rs.BookID, rs.ChapterID, rs.SceneID, N'Relationship', rs.CharacterRelationshipID,
|
|
wt.WarningTypeID, ws.WarningSeverityID,
|
|
N'Possible emotional transition issue. Relationship intensity changes sharply between scenes.',
|
|
CONCAT(rs.CharacterAName, N' / ', rs.CharacterBName, N' changes from ', rs.PreviousIntensity, N' to ', rs.Intensity, N'. Previous scene: ', rs.PreviousSceneTitle, N'.')
|
|
FROM RelationshipSteps rs
|
|
CROSS JOIN dbo.WarningTypes wt
|
|
CROSS JOIN dbo.WarningSeverities ws
|
|
WHERE wt.TypeName = N'Abrupt Relationship Change' AND ws.SeverityName = N'Info'
|
|
AND rs.Intensity IS NOT NULL AND rs.PreviousIntensity IS NOT NULL AND ABS(rs.Intensity - rs.PreviousIntensity) >= 5
|
|
AND (@BookID IS NULL OR rs.BookID = @BookID) AND (@SceneID IS NULL OR rs.SceneID = @SceneID)
|
|
AND NOT EXISTS (SELECT 1 FROM dbo.ContinuityWarnings existing WHERE existing.ProjectID = rs.ProjectID AND existing.SceneID = rs.SceneID AND existing.EntityType = N'Relationship' AND existing.EntityID = rs.CharacterRelationshipID AND existing.WarningTypeID = wt.WarningTypeID AND existing.IsDismissed = 0 AND existing.IsIntentional = 0);
|
|
|
|
;WITH Appearances AS
|
|
(
|
|
SELECT sc.CharacterID, ch.CharacterName, nso.ProjectID, nso.BookID, nso.ChapterID, sc.SceneID, nso.GlobalSceneIndex,
|
|
LAG(nso.GlobalSceneIndex) OVER (PARTITION BY sc.CharacterID ORDER BY nso.GlobalSceneIndex) AS PreviousIndex
|
|
FROM dbo.SceneCharacters sc
|
|
INNER JOIN dbo.Characters ch ON ch.CharacterID = sc.CharacterID
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = sc.SceneID
|
|
WHERE ch.ProjectID = @ProjectID AND ch.IsArchived = 0
|
|
)
|
|
INSERT dbo.ContinuityWarnings (ProjectID, BookID, ChapterID, SceneID, EntityType, EntityID, WarningTypeID, WarningSeverityID, Message, Details)
|
|
SELECT ProjectID, BookID, ChapterID, SceneID, N'Character', CharacterID, wt.WarningTypeID, ws.WarningSeverityID,
|
|
CONCAT(N'Possible review: ', CharacterName, N' is absent for ', GlobalSceneIndex - PreviousIndex - 1, N' narrative scenes.'),
|
|
N'Long absences are sometimes useful, but this may be worth checking after a major beat.'
|
|
FROM Appearances
|
|
CROSS JOIN dbo.WarningTypes wt
|
|
CROSS JOIN dbo.WarningSeverities ws
|
|
WHERE wt.TypeName = N'Long Character Absence' AND ws.SeverityName = N'Info'
|
|
AND PreviousIndex IS NOT NULL AND GlobalSceneIndex - PreviousIndex - 1 >= 12
|
|
AND (@BookID IS NULL OR BookID = @BookID) AND (@SceneID IS NULL OR SceneID = @SceneID)
|
|
AND NOT EXISTS (SELECT 1 FROM dbo.ContinuityWarnings existing WHERE existing.ProjectID = Appearances.ProjectID AND existing.SceneID = Appearances.SceneID AND existing.EntityType = N'Character' AND existing.EntityID = Appearances.CharacterID AND existing.WarningTypeID = wt.WarningTypeID AND existing.IsDismissed = 0 AND existing.IsIntentional = 0);
|
|
|
|
;WITH CharacterUsage AS
|
|
(
|
|
SELECT ch.ProjectID, ch.CharacterID, ch.CharacterName,
|
|
COUNT(DISTINCT sc.SceneID) AS AppearanceCount,
|
|
COUNT(DISTINCT cr.CharacterRelationshipID) AS RelationshipCount
|
|
FROM dbo.Characters ch
|
|
LEFT JOIN dbo.SceneCharacters sc ON sc.CharacterID = ch.CharacterID
|
|
LEFT JOIN dbo.CharacterRelationships cr ON cr.ProjectID = ch.ProjectID
|
|
AND cr.IsArchived = 0
|
|
AND ch.CharacterID IN (cr.CharacterAID, cr.CharacterBID)
|
|
WHERE ch.ProjectID = @ProjectID AND ch.IsArchived = 0 AND @SceneID IS NULL
|
|
GROUP BY ch.ProjectID, ch.CharacterID, ch.CharacterName
|
|
HAVING COUNT(DISTINCT cr.CharacterRelationshipID) >= 2 AND COUNT(DISTINCT sc.SceneID) <= 1
|
|
)
|
|
INSERT dbo.ContinuityWarnings (ProjectID, EntityType, EntityID, WarningTypeID, WarningSeverityID, Message, Details)
|
|
SELECT ProjectID, N'Character', CharacterID, wt.WarningTypeID, ws.WarningSeverityID,
|
|
CONCAT(N'Possible review: ', CharacterName, N' has several relationship links but very few scene appearances.'),
|
|
N'This may be intentional, but the character could be underused relative to their story connections.'
|
|
FROM CharacterUsage
|
|
CROSS JOIN dbo.WarningTypes wt
|
|
CROSS JOIN dbo.WarningSeverities ws
|
|
WHERE wt.TypeName = N'Character Underused' AND ws.SeverityName = N'Info'
|
|
AND NOT EXISTS (SELECT 1 FROM dbo.ContinuityWarnings existing WHERE existing.ProjectID = CharacterUsage.ProjectID AND existing.EntityType = N'Character' AND existing.EntityID = CharacterUsage.CharacterID AND existing.WarningTypeID = wt.WarningTypeID AND existing.IsDismissed = 0 AND existing.IsIntentional = 0);
|
|
|
|
;WITH SceneCounts AS
|
|
(
|
|
SELECT nso.ProjectID, nso.BookID, nso.ChapterID, nso.SceneID, nso.SceneTitle, COUNT(*) AS CharacterCount
|
|
FROM dbo.SceneCharacters sc
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = sc.SceneID
|
|
WHERE nso.ProjectID = @ProjectID AND (@BookID IS NULL OR nso.BookID = @BookID) AND (@SceneID IS NULL OR nso.SceneID = @SceneID)
|
|
GROUP BY nso.ProjectID, nso.BookID, nso.ChapterID, nso.SceneID, nso.SceneTitle
|
|
HAVING COUNT(*) >= 7
|
|
)
|
|
INSERT dbo.ContinuityWarnings (ProjectID, BookID, ChapterID, SceneID, EntityType, EntityID, WarningTypeID, WarningSeverityID, Message, Details)
|
|
SELECT ProjectID, BookID, ChapterID, SceneID, N'Scene', SceneID, wt.WarningTypeID, ws.WarningSeverityID,
|
|
CONCAT(N'Possible review: this scene has ', CharacterCount, N' character appearances.'),
|
|
N'Busy scenes can work well, but may need extra clarity around focus and blocking.'
|
|
FROM SceneCounts
|
|
CROSS JOIN dbo.WarningTypes wt
|
|
CROSS JOIN dbo.WarningSeverities ws
|
|
WHERE wt.TypeName = N'Scene Character Overload' AND ws.SeverityName = N'Info'
|
|
AND NOT EXISTS (SELECT 1 FROM dbo.ContinuityWarnings existing WHERE existing.ProjectID = SceneCounts.ProjectID AND existing.SceneID = SceneCounts.SceneID AND existing.EntityType = N'Scene' AND existing.EntityID = SceneCounts.SceneID AND existing.WarningTypeID = wt.WarningTypeID AND existing.IsDismissed = 0 AND existing.IsIntentional = 0);
|
|
|
|
;WITH Knowledge AS
|
|
(
|
|
SELECT ck.CharacterID, ch.CharacterName, ck.StoryAssetID, ck.PlotThreadID, ck.SceneID, nso.ProjectID, nso.BookID, nso.ChapterID, nso.GlobalSceneIndex,
|
|
MIN(nso.GlobalSceneIndex) OVER (PARTITION BY ck.CharacterID, ISNULL(ck.StoryAssetID, 0), ISNULL(ck.PlotThreadID, 0)) AS FirstKnownIndex
|
|
FROM dbo.CharacterKnowledge ck
|
|
INNER JOIN dbo.Characters ch ON ch.CharacterID = ck.CharacterID
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = ck.SceneID
|
|
WHERE ch.ProjectID = @ProjectID
|
|
),
|
|
Candidate AS
|
|
(
|
|
SELECT DISTINCT sc.CharacterID, ch.CharacterName, te.PlotThreadID, ae.StoryAssetID, nso.ProjectID, nso.BookID, nso.ChapterID, sc.SceneID, nso.GlobalSceneIndex
|
|
FROM dbo.SceneCharacters sc
|
|
INNER JOIN dbo.Characters ch ON ch.CharacterID = sc.CharacterID
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = sc.SceneID
|
|
LEFT JOIN dbo.ThreadEvents te ON te.SceneID = sc.SceneID
|
|
LEFT JOIN dbo.AssetEvents ae ON ae.SceneID = sc.SceneID
|
|
WHERE ch.ProjectID = @ProjectID AND (@BookID IS NULL OR nso.BookID = @BookID) AND (@SceneID IS NULL OR sc.SceneID = @SceneID)
|
|
AND (te.PlotThreadID IS NOT NULL OR ae.StoryAssetID IS NOT NULL)
|
|
)
|
|
INSERT dbo.ContinuityWarnings (ProjectID, BookID, ChapterID, SceneID, EntityType, EntityID, WarningTypeID, WarningSeverityID, Message, Details)
|
|
SELECT c.ProjectID, c.BookID, c.ChapterID, c.SceneID, N'Character', c.CharacterID, wt.WarningTypeID, ws.WarningSeverityID,
|
|
CONCAT(N'Possible review: ', c.CharacterName, N' appears in a scene involving information without an earlier knowledge event.'),
|
|
N'This is advisory only. Add a knowledge event if the character is meant to know this, or mark the warning intentional.'
|
|
FROM Candidate c
|
|
CROSS JOIN dbo.WarningTypes wt
|
|
CROSS JOIN dbo.WarningSeverities ws
|
|
WHERE wt.TypeName = N'Knowledge Continuity' AND ws.SeverityName = N'Info'
|
|
AND NOT EXISTS
|
|
(
|
|
SELECT 1 FROM Knowledge k
|
|
WHERE k.CharacterID = c.CharacterID
|
|
AND ((c.StoryAssetID IS NOT NULL AND k.StoryAssetID = c.StoryAssetID) OR (c.PlotThreadID IS NOT NULL AND k.PlotThreadID = c.PlotThreadID))
|
|
AND k.FirstKnownIndex <= c.GlobalSceneIndex
|
|
)
|
|
AND NOT EXISTS (SELECT 1 FROM dbo.ContinuityWarnings existing WHERE existing.ProjectID = c.ProjectID AND existing.SceneID = c.SceneID AND existing.EntityType = N'Character' AND existing.EntityID = c.CharacterID AND existing.WarningTypeID = wt.WarningTypeID AND existing.IsDismissed = 0 AND existing.IsIntentional = 0);
|
|
END;
|
|
GO
|