Merge remote-tracking branch 'origin/NewStory' into NewStory
This commit is contained in:
@@ -232,6 +232,28 @@ MonoBehaviour:
|
|||||||
spritePortrait: {fileID: 0}
|
spritePortrait: {fileID: 0}
|
||||||
alternatePortraits: []
|
alternatePortraits: []
|
||||||
spritePortraits: []
|
spritePortraits: []
|
||||||
|
- id: 10
|
||||||
|
fields:
|
||||||
|
- title: Name
|
||||||
|
value: Begger
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Pictures
|
||||||
|
value: '[]'
|
||||||
|
type: 3
|
||||||
|
typeString: CustomFieldType_Files
|
||||||
|
- title: Description
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: IsPlayer
|
||||||
|
value: False
|
||||||
|
type: 2
|
||||||
|
typeString: CustomFieldType_Boolean
|
||||||
|
portrait: {fileID: 0}
|
||||||
|
spritePortrait: {fileID: 0}
|
||||||
|
alternatePortraits: []
|
||||||
|
spritePortraits: []
|
||||||
items:
|
items:
|
||||||
- id: 14
|
- id: 14
|
||||||
fields:
|
fields:
|
||||||
@@ -23782,6 +23804,581 @@ MonoBehaviour:
|
|||||||
height: 30
|
height: 30
|
||||||
canvasScrollPosition: {x: 0, y: 0}
|
canvasScrollPosition: {x: 0, y: 0}
|
||||||
canvasZoom: 1
|
canvasZoom: 1
|
||||||
|
- id: 96
|
||||||
|
fields:
|
||||||
|
- title: Title
|
||||||
|
value: CH02/RUINEDTOWN/BeggerDialogue
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Pictures
|
||||||
|
value: '[]'
|
||||||
|
type: 3
|
||||||
|
typeString: CustomFieldType_Files
|
||||||
|
- title: Description
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Actor
|
||||||
|
value: 1
|
||||||
|
type: 5
|
||||||
|
typeString: CustomFieldType_Actor
|
||||||
|
- title: Conversant
|
||||||
|
value: 2
|
||||||
|
type: 5
|
||||||
|
typeString: CustomFieldType_Actor
|
||||||
|
overrideSettings:
|
||||||
|
useOverrides: 0
|
||||||
|
overrideSubtitleSettings: 0
|
||||||
|
showNPCSubtitlesDuringLine: 1
|
||||||
|
showNPCSubtitlesWithResponses: 1
|
||||||
|
showPCSubtitlesDuringLine: 0
|
||||||
|
skipPCSubtitleAfterResponseMenu: 0
|
||||||
|
subtitleCharsPerSecond: 30
|
||||||
|
minSubtitleSeconds: 2
|
||||||
|
continueButton: 0
|
||||||
|
overrideSequenceSettings: 0
|
||||||
|
defaultSequence:
|
||||||
|
defaultPlayerSequence:
|
||||||
|
defaultResponseMenuSequence:
|
||||||
|
overrideInputSettings: 0
|
||||||
|
alwaysForceResponseMenu: 1
|
||||||
|
includeInvalidEntries: 0
|
||||||
|
responseTimeout: 0
|
||||||
|
cancelSubtitle:
|
||||||
|
key: 27
|
||||||
|
buttonName:
|
||||||
|
cancelConversation:
|
||||||
|
key: 27
|
||||||
|
buttonName:
|
||||||
|
nodeColor:
|
||||||
|
dialogueEntries:
|
||||||
|
- id: 0
|
||||||
|
fields:
|
||||||
|
- title: Title
|
||||||
|
value: START
|
||||||
|
type: 0
|
||||||
|
typeString:
|
||||||
|
- title: Pictures
|
||||||
|
value: '[]'
|
||||||
|
type: 3
|
||||||
|
typeString: CustomFieldType_Files
|
||||||
|
- title: Description
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString:
|
||||||
|
- title: Actor
|
||||||
|
value: 1
|
||||||
|
type: 5
|
||||||
|
typeString: CustomFieldType_Actor
|
||||||
|
- title: Conversant
|
||||||
|
value: 2
|
||||||
|
type: 5
|
||||||
|
typeString: CustomFieldType_Actor
|
||||||
|
- title: Menu Text
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString:
|
||||||
|
- title: Dialogue Text
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString:
|
||||||
|
- title: Parenthetical
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString:
|
||||||
|
- title: Audio Files
|
||||||
|
value: '[]'
|
||||||
|
type: 3
|
||||||
|
typeString: CustomFieldType_Files
|
||||||
|
- title: Video File
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString:
|
||||||
|
- title: Sequence
|
||||||
|
value: None()
|
||||||
|
type: 0
|
||||||
|
typeString:
|
||||||
|
- title: pl
|
||||||
|
value:
|
||||||
|
type: 4
|
||||||
|
typeString: CustomFieldType_Localization
|
||||||
|
conversationID: 96
|
||||||
|
isRoot: 0
|
||||||
|
isGroup: 0
|
||||||
|
nodeColor:
|
||||||
|
delaySimStatus: 0
|
||||||
|
falseConditionAction:
|
||||||
|
conditionPriority: 2
|
||||||
|
outgoingLinks:
|
||||||
|
- originConversationID: 96
|
||||||
|
originDialogueID: 0
|
||||||
|
destinationConversationID: 96
|
||||||
|
destinationDialogueID: 1
|
||||||
|
isConnector: 0
|
||||||
|
priority: 2
|
||||||
|
conditionsString:
|
||||||
|
userScript:
|
||||||
|
onExecute:
|
||||||
|
m_PersistentCalls:
|
||||||
|
m_Calls: []
|
||||||
|
canvasRect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 20
|
||||||
|
y: 30
|
||||||
|
width: 160
|
||||||
|
height: 30
|
||||||
|
- id: 1
|
||||||
|
fields:
|
||||||
|
- title: Title
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Pictures
|
||||||
|
value: '[]'
|
||||||
|
type: 3
|
||||||
|
typeString: CustomFieldType_Files
|
||||||
|
- title: Description
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Actor
|
||||||
|
value: 10
|
||||||
|
type: 5
|
||||||
|
typeString: CustomFieldType_Actor
|
||||||
|
- title: Conversant
|
||||||
|
value: 1
|
||||||
|
type: 5
|
||||||
|
typeString: CustomFieldType_Actor
|
||||||
|
- title: Menu Text
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Dialogue Text
|
||||||
|
value: Mhm... Another lost soul wanders through the streets of Ancaran in
|
||||||
|
search of hope...
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Parenthetical
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Audio Files
|
||||||
|
value: '[]'
|
||||||
|
type: 3
|
||||||
|
typeString: CustomFieldType_Files
|
||||||
|
- title: Video File
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Sequence
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: pl
|
||||||
|
value:
|
||||||
|
type: 4
|
||||||
|
typeString: CustomFieldType_Localization
|
||||||
|
conversationID: 96
|
||||||
|
isRoot: 0
|
||||||
|
isGroup: 0
|
||||||
|
nodeColor:
|
||||||
|
delaySimStatus: 0
|
||||||
|
falseConditionAction: Block
|
||||||
|
conditionPriority: 2
|
||||||
|
outgoingLinks:
|
||||||
|
- originConversationID: 96
|
||||||
|
originDialogueID: 1
|
||||||
|
destinationConversationID: 96
|
||||||
|
destinationDialogueID: 2
|
||||||
|
isConnector: 0
|
||||||
|
priority: 2
|
||||||
|
conditionsString:
|
||||||
|
userScript:
|
||||||
|
onExecute:
|
||||||
|
m_PersistentCalls:
|
||||||
|
m_Calls: []
|
||||||
|
canvasRect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 19
|
||||||
|
y: 87
|
||||||
|
width: 160
|
||||||
|
height: 30
|
||||||
|
- id: 2
|
||||||
|
fields:
|
||||||
|
- title: Title
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Pictures
|
||||||
|
value: '[]'
|
||||||
|
type: 3
|
||||||
|
typeString: CustomFieldType_Files
|
||||||
|
- title: Description
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Actor
|
||||||
|
value: 1
|
||||||
|
type: 5
|
||||||
|
typeString: CustomFieldType_Actor
|
||||||
|
- title: Conversant
|
||||||
|
value: 10
|
||||||
|
type: 5
|
||||||
|
typeString: CustomFieldType_Actor
|
||||||
|
- title: Menu Text
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Dialogue Text
|
||||||
|
value: Hail, old man.
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Parenthetical
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Audio Files
|
||||||
|
value: '[]'
|
||||||
|
type: 3
|
||||||
|
typeString: CustomFieldType_Files
|
||||||
|
- title: Video File
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Sequence
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: pl
|
||||||
|
value:
|
||||||
|
type: 4
|
||||||
|
typeString: CustomFieldType_Localization
|
||||||
|
conversationID: 96
|
||||||
|
isRoot: 0
|
||||||
|
isGroup: 0
|
||||||
|
nodeColor:
|
||||||
|
delaySimStatus: 0
|
||||||
|
falseConditionAction: Block
|
||||||
|
conditionPriority: 2
|
||||||
|
outgoingLinks:
|
||||||
|
- originConversationID: 96
|
||||||
|
originDialogueID: 2
|
||||||
|
destinationConversationID: 96
|
||||||
|
destinationDialogueID: 3
|
||||||
|
isConnector: 0
|
||||||
|
priority: 2
|
||||||
|
conditionsString:
|
||||||
|
userScript:
|
||||||
|
onExecute:
|
||||||
|
m_PersistentCalls:
|
||||||
|
m_Calls: []
|
||||||
|
canvasRect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 19
|
||||||
|
y: 130
|
||||||
|
width: 160
|
||||||
|
height: 30
|
||||||
|
- id: 3
|
||||||
|
fields:
|
||||||
|
- title: Title
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Pictures
|
||||||
|
value: '[]'
|
||||||
|
type: 3
|
||||||
|
typeString: CustomFieldType_Files
|
||||||
|
- title: Description
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Actor
|
||||||
|
value: 10
|
||||||
|
type: 5
|
||||||
|
typeString: CustomFieldType_Actor
|
||||||
|
- title: Conversant
|
||||||
|
value: 1
|
||||||
|
type: 5
|
||||||
|
typeString: CustomFieldType_Actor
|
||||||
|
- title: Menu Text
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Dialogue Text
|
||||||
|
value: Another fool...! There is no hope in Ancaran! Nor anywhere else. We
|
||||||
|
were all betrayed. Abandoned.
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Parenthetical
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Audio Files
|
||||||
|
value: '[]'
|
||||||
|
type: 3
|
||||||
|
typeString: CustomFieldType_Files
|
||||||
|
- title: Video File
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Sequence
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: pl
|
||||||
|
value:
|
||||||
|
type: 4
|
||||||
|
typeString: CustomFieldType_Localization
|
||||||
|
conversationID: 96
|
||||||
|
isRoot: 0
|
||||||
|
isGroup: 0
|
||||||
|
nodeColor:
|
||||||
|
delaySimStatus: 0
|
||||||
|
falseConditionAction: Block
|
||||||
|
conditionPriority: 2
|
||||||
|
outgoingLinks:
|
||||||
|
- originConversationID: 96
|
||||||
|
originDialogueID: 3
|
||||||
|
destinationConversationID: 96
|
||||||
|
destinationDialogueID: 4
|
||||||
|
isConnector: 0
|
||||||
|
priority: 2
|
||||||
|
conditionsString:
|
||||||
|
userScript:
|
||||||
|
onExecute:
|
||||||
|
m_PersistentCalls:
|
||||||
|
m_Calls: []
|
||||||
|
canvasRect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 19
|
||||||
|
y: 180
|
||||||
|
width: 160
|
||||||
|
height: 30
|
||||||
|
- id: 4
|
||||||
|
fields:
|
||||||
|
- title: Title
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Pictures
|
||||||
|
value: '[]'
|
||||||
|
type: 3
|
||||||
|
typeString: CustomFieldType_Files
|
||||||
|
- title: Description
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Actor
|
||||||
|
value: 1
|
||||||
|
type: 5
|
||||||
|
typeString: CustomFieldType_Actor
|
||||||
|
- title: Conversant
|
||||||
|
value: 10
|
||||||
|
type: 5
|
||||||
|
typeString: CustomFieldType_Actor
|
||||||
|
- title: Menu Text
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Dialogue Text
|
||||||
|
value: Who betrayed you?
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Parenthetical
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Audio Files
|
||||||
|
value: '[]'
|
||||||
|
type: 3
|
||||||
|
typeString: CustomFieldType_Files
|
||||||
|
- title: Video File
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Sequence
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: pl
|
||||||
|
value:
|
||||||
|
type: 4
|
||||||
|
typeString: CustomFieldType_Localization
|
||||||
|
conversationID: 96
|
||||||
|
isRoot: 0
|
||||||
|
isGroup: 0
|
||||||
|
nodeColor:
|
||||||
|
delaySimStatus: 0
|
||||||
|
falseConditionAction: Block
|
||||||
|
conditionPriority: 2
|
||||||
|
outgoingLinks:
|
||||||
|
- originConversationID: 96
|
||||||
|
originDialogueID: 4
|
||||||
|
destinationConversationID: 96
|
||||||
|
destinationDialogueID: 5
|
||||||
|
isConnector: 0
|
||||||
|
priority: 2
|
||||||
|
conditionsString:
|
||||||
|
userScript:
|
||||||
|
onExecute:
|
||||||
|
m_PersistentCalls:
|
||||||
|
m_Calls: []
|
||||||
|
canvasRect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 19
|
||||||
|
y: 230
|
||||||
|
width: 160
|
||||||
|
height: 30
|
||||||
|
- id: 5
|
||||||
|
fields:
|
||||||
|
- title: Title
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Pictures
|
||||||
|
value: '[]'
|
||||||
|
type: 3
|
||||||
|
typeString: CustomFieldType_Files
|
||||||
|
- title: Description
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Actor
|
||||||
|
value: 10
|
||||||
|
type: 5
|
||||||
|
typeString: CustomFieldType_Actor
|
||||||
|
- title: Conversant
|
||||||
|
value: 1
|
||||||
|
type: 5
|
||||||
|
typeString: CustomFieldType_Actor
|
||||||
|
- title: Menu Text
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Dialogue Text
|
||||||
|
value: Oh, the sun, and the dust, and the shadows... All is done now...
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Parenthetical
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Audio Files
|
||||||
|
value: '[]'
|
||||||
|
type: 3
|
||||||
|
typeString: CustomFieldType_Files
|
||||||
|
- title: Video File
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Sequence
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: pl
|
||||||
|
value:
|
||||||
|
type: 4
|
||||||
|
typeString: CustomFieldType_Localization
|
||||||
|
conversationID: 96
|
||||||
|
isRoot: 0
|
||||||
|
isGroup: 0
|
||||||
|
nodeColor:
|
||||||
|
delaySimStatus: 0
|
||||||
|
falseConditionAction: Block
|
||||||
|
conditionPriority: 2
|
||||||
|
outgoingLinks:
|
||||||
|
- originConversationID: 96
|
||||||
|
originDialogueID: 5
|
||||||
|
destinationConversationID: 96
|
||||||
|
destinationDialogueID: 6
|
||||||
|
isConnector: 0
|
||||||
|
priority: 2
|
||||||
|
conditionsString:
|
||||||
|
userScript:
|
||||||
|
onExecute:
|
||||||
|
m_PersistentCalls:
|
||||||
|
m_Calls: []
|
||||||
|
canvasRect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 19
|
||||||
|
y: 280
|
||||||
|
width: 160
|
||||||
|
height: 30
|
||||||
|
- id: 6
|
||||||
|
fields:
|
||||||
|
- title: Title
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Pictures
|
||||||
|
value: '[]'
|
||||||
|
type: 3
|
||||||
|
typeString: CustomFieldType_Files
|
||||||
|
- title: Description
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Actor
|
||||||
|
value: 1
|
||||||
|
type: 5
|
||||||
|
typeString: CustomFieldType_Actor
|
||||||
|
- title: Conversant
|
||||||
|
value: 10
|
||||||
|
type: 5
|
||||||
|
typeString: CustomFieldType_Actor
|
||||||
|
- title: Menu Text
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Dialogue Text
|
||||||
|
value: Nothing but a ranting lunatic.
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Parenthetical
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Audio Files
|
||||||
|
value: '[]'
|
||||||
|
type: 3
|
||||||
|
typeString: CustomFieldType_Files
|
||||||
|
- title: Video File
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: Sequence
|
||||||
|
value:
|
||||||
|
type: 0
|
||||||
|
typeString: CustomFieldType_Text
|
||||||
|
- title: pl
|
||||||
|
value:
|
||||||
|
type: 4
|
||||||
|
typeString: CustomFieldType_Localization
|
||||||
|
- title: EventGuid
|
||||||
|
value: b6b6048b-ca0a-41eb-997f-2fd9796542ae
|
||||||
|
type: 0
|
||||||
|
typeString:
|
||||||
|
conversationID: 96
|
||||||
|
isRoot: 0
|
||||||
|
isGroup: 0
|
||||||
|
nodeColor:
|
||||||
|
delaySimStatus: 0
|
||||||
|
falseConditionAction: Block
|
||||||
|
conditionPriority: 2
|
||||||
|
outgoingLinks: []
|
||||||
|
conditionsString:
|
||||||
|
userScript:
|
||||||
|
onExecute:
|
||||||
|
m_PersistentCalls:
|
||||||
|
m_Calls: []
|
||||||
|
canvasRect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 19
|
||||||
|
y: 330
|
||||||
|
width: 160
|
||||||
|
height: 30
|
||||||
|
canvasScrollPosition: {x: 0, y: 0}
|
||||||
|
canvasZoom: 1
|
||||||
syncInfo:
|
syncInfo:
|
||||||
syncActors: 0
|
syncActors: 0
|
||||||
syncItems: 0
|
syncItems: 0
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,74 +1,102 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
|
||||||
using PixelCrushers; // For Saver
|
using PixelCrushers; // For Saver
|
||||||
|
using Sirenix.OdinInspector; // For ReadOnly/Button attributes (Optional: remove if not using Odin)
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Random = UnityEngine.Random;
|
using Random = UnityEngine.Random;
|
||||||
|
|
||||||
namespace Beyond
|
namespace Beyond // Ensure this namespace matches your project
|
||||||
{
|
{
|
||||||
[RequireComponent(typeof(Collider))]
|
[RequireComponent(typeof(Collider))]
|
||||||
public class BarkPlayer : Saver
|
public class BarkPlayer : Saver
|
||||||
{
|
{
|
||||||
[Header("Bark Configuration")]
|
#region Inspector Fields
|
||||||
[Tooltip("The index of the BarkEntry in BarkManager's m_barks list.")]
|
|
||||||
public int barkManagerEntryIndex = 0;
|
|
||||||
|
|
||||||
[Tooltip("Delay in seconds after one bark's audio finishes before starting the next.")]
|
[Header("Bark Configuration")]
|
||||||
|
[Tooltip("The indices of the BarkEntries in BarkManager's list to play sequentially.")]
|
||||||
|
public int[] barkManagerEntryIndices = new int[0];
|
||||||
|
|
||||||
|
[Tooltip("Delay in seconds after one bark's audio finishes before starting the next bark WITHIN THE SAME entry sequence.")]
|
||||||
|
[Min(0f)]
|
||||||
public float delayBetweenBarks = 0.5f;
|
public float delayBetweenBarks = 0.5f;
|
||||||
|
|
||||||
[Tooltip("Play barks within the entry in a random order (shuffled once on start/load).")]
|
[Tooltip("Delay in seconds after ALL barks in one entry sequence finish before starting the NEXT entry sequence.")]
|
||||||
public bool shuffleOrder = false;
|
[Min(0f)]
|
||||||
|
public float delayBetweenEntries = 5.0f;
|
||||||
|
|
||||||
[Tooltip("Keep playing barks (looping the sequence if necessary) as long as an object on a triggering layer stays inside.")]
|
[Tooltip("Play barks within each entry in a random order (shuffled when the entry starts).")]
|
||||||
public bool loopWhileInside = true;
|
public bool shuffleOrder = false;
|
||||||
|
|
||||||
[Header("Trigger Settings")]
|
[Header("Trigger Settings")]
|
||||||
[Tooltip("Layers that can activate this bark trigger.")]
|
[Tooltip("Layers that can activate this bark trigger.")]
|
||||||
public LayerMask triggeringLayers; // User configures this in Inspector
|
public LayerMask triggeringLayers;
|
||||||
|
|
||||||
[Header("State (Read Only)")]
|
[Header("Activation")]
|
||||||
[SerializeField, ReadOnly(true)]
|
[Tooltip("If true, the BarkPlayer will be active automatically when the game starts or when loaded without existing save data for it.")]
|
||||||
private int nextBarkSequenceIndex = 0;
|
public bool startAutomaticallyOnLoad = false;
|
||||||
[SerializeField, ReadOnly(true)]
|
|
||||||
private bool isTriggeringObjectInside = false; // Renamed for clarity
|
|
||||||
|
|
||||||
// --- Internal Vars ---
|
// Runtime state fields remain private but visible for debugging if needed
|
||||||
// Removed PLAYER_LAYER_NAME constant
|
[Header("Runtime State (Read Only)")]
|
||||||
private List<int> playbackOrder;
|
[SerializeField, ReadOnly] // Use Sirenix ReadOnly or remove if not using Odin
|
||||||
|
private bool _runtime_IsStarted = false; // Tracks the current operational state
|
||||||
|
|
||||||
|
[SerializeField, ReadOnly]
|
||||||
|
private int currentEntryArrayIndex = 0; // Index for barkManagerEntryIndices array
|
||||||
|
|
||||||
|
[SerializeField, ReadOnly]
|
||||||
|
private int nextBarkSequenceIndex = 0; // Index for playbackOrder (within current entry)
|
||||||
|
|
||||||
|
[SerializeField, ReadOnly]
|
||||||
|
private bool isTriggeringObjectInside = false; // Is a valid object currently inside?
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Internal Variables
|
||||||
|
|
||||||
|
private List<int> playbackOrder; // Stores the bark order for the CURRENTLY active entry
|
||||||
private bool isInitialized = false;
|
private bool isInitialized = false;
|
||||||
private Collider triggerCollider;
|
private Collider triggerCollider;
|
||||||
private BarkManager barkManager;
|
private BarkManager barkManager; // Cached reference
|
||||||
private Coroutine currentPlaybackCoroutine = null;
|
private Coroutine currentPlaybackCoroutine = null;
|
||||||
private Transform currentTriggererTransform = null; // Store the transform of the object inside
|
private Transform currentTriggererTransform = null; // Transform of the object inside
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Save Data Structure
|
||||||
|
|
||||||
// --- Save Data --- (Remains the same)
|
|
||||||
[System.Serializable]
|
[System.Serializable]
|
||||||
public class BarkPlayerData { /* ... same as before ... */
|
public class BarkPlayerData
|
||||||
public int savedNextBarkSequenceIndex;
|
{
|
||||||
public List<int> savedPlaybackOrder;
|
public bool savedIsStarted; // Tracks the saved state from _runtime_IsStarted
|
||||||
public bool wasShuffled;
|
public int savedCurrentEntryArrayIndex;
|
||||||
|
public int savedNextBarkSequenceIndex;
|
||||||
|
public bool wasShuffled; // Tracks shuffle setting at time of save
|
||||||
}
|
}
|
||||||
private BarkPlayerData m_saveData = new BarkPlayerData();
|
private BarkPlayerData m_saveData = new BarkPlayerData();
|
||||||
|
|
||||||
// --- Methods ---
|
#endregion
|
||||||
|
|
||||||
|
#region Unity Lifecycle Methods
|
||||||
|
|
||||||
public override void Awake()
|
public override void Awake()
|
||||||
{
|
{
|
||||||
base.Awake();
|
// Set initial state based on Inspector setting *before* base.Awake potentially calls ApplyData
|
||||||
|
// If save data exists, ApplyData will overwrite this.
|
||||||
|
_runtime_IsStarted = startAutomaticallyOnLoad;
|
||||||
|
|
||||||
|
base.Awake(); // This might call ApplyData via Save System
|
||||||
|
|
||||||
|
// --- Component & Initial Setup ---
|
||||||
triggerCollider = GetComponent<Collider>();
|
triggerCollider = GetComponent<Collider>();
|
||||||
if (!triggerCollider.isTrigger)
|
if (!triggerCollider.isTrigger)
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"BarkPlayer on {gameObject.name}: Collider is not set to 'Is Trigger'. Forcing it.", this);
|
Debug.LogWarning($"BarkPlayer on {gameObject.name}: Collider was not set to 'Is Trigger'. Forcing it.", this);
|
||||||
triggerCollider.isTrigger = true;
|
triggerCollider.isTrigger = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Set Default LayerMask if Unassigned ---
|
// --- Default LayerMask ---
|
||||||
if (triggeringLayers.value == 0) // LayerMask is empty/unassigned in inspector
|
if (triggeringLayers.value == 0) // LayerMask is empty/unassigned
|
||||||
{
|
{
|
||||||
int playerLayer = LayerMask.NameToLayer("Player");
|
int playerLayer = LayerMask.NameToLayer("Player");
|
||||||
if (playerLayer != -1)
|
if (playerLayer != -1)
|
||||||
@@ -78,245 +106,443 @@ namespace Beyond
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Layer "Player" doesn't exist, and no layers were assigned. Log an error.
|
Debug.LogError($"BarkPlayer ({gameObject.name}): Triggering Layers is not set in the inspector, and the default 'Player' layer was not found. This trigger will likely not activate.", this);
|
||||||
Debug.LogError($"BarkPlayer on {gameObject.name}: Triggering Layers is not set in the inspector, and the default 'Player' layer was not found. This trigger will likely not activate.", this);
|
|
||||||
// Leave triggeringLayers as 0 (Nothing)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// --- End LayerMask Default ---
|
|
||||||
|
|
||||||
|
// Final state of _runtime_IsStarted reflects Inspector default OR loaded save data.
|
||||||
// Initialize save data defaults
|
|
||||||
m_saveData.savedNextBarkSequenceIndex = 0;
|
|
||||||
m_saveData.savedPlaybackOrder = null;
|
|
||||||
m_saveData.wasShuffled = shuffleOrder;
|
|
||||||
nextBarkSequenceIndex = 0;
|
|
||||||
isTriggeringObjectInside = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
|
// --- Get BarkManager Instance ---
|
||||||
barkManager = BarkManager.Instance;
|
barkManager = BarkManager.Instance;
|
||||||
if (barkManager == null)
|
if (barkManager == null)
|
||||||
{
|
{
|
||||||
Debug.LogError($"BarkPlayer on {gameObject.name}: Could not find BarkManager instance!", this);
|
Debug.LogError($"BarkPlayer ({gameObject.name}): Could not find BarkManager instance! Disabling component.", this);
|
||||||
enabled = false;
|
enabled = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
InitializePlaybackOrder();
|
// --- Apply Loaded Indices ---
|
||||||
isInitialized = true;
|
// Ensure array index is valid even if array size changed since saving
|
||||||
}
|
currentEntryArrayIndex = Mathf.Clamp(m_saveData.savedCurrentEntryArrayIndex, 0, Mathf.Max(0, barkManagerEntryIndices.Length - 1));
|
||||||
|
// nextBarkSequenceIndex will be applied when PrepareEntrySequence is called
|
||||||
|
|
||||||
// InitializePlaybackOrder() remains the same as before
|
// --- Validate Entry Indices ---
|
||||||
|
if (barkManagerEntryIndices == null || barkManagerEntryIndices.Length == 0)
|
||||||
private void InitializePlaybackOrder()
|
|
||||||
{
|
|
||||||
if (barkManager == null) return;
|
|
||||||
|
|
||||||
int barkCount = barkManager.GetBarkCountInEntry(barkManagerEntryIndex);
|
|
||||||
if (barkCount <= 0)
|
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"BarkPlayer on {gameObject.name}: BarkEntry {barkManagerEntryIndex} has no barks. Disabling.", this);
|
Debug.LogWarning($"BarkPlayer ({gameObject.name}): No Bark Manager Entry Indices provided. Disabling.", this);
|
||||||
playbackOrder = new List<int>();
|
|
||||||
enabled = false;
|
enabled = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
for (int i = 0; i < barkManagerEntryIndices.Length; i++)
|
||||||
bool useLoadedOrder = m_saveData.savedPlaybackOrder != null &&
|
|
||||||
m_saveData.wasShuffled == shuffleOrder &&
|
|
||||||
m_saveData.savedPlaybackOrder.Count == barkCount;
|
|
||||||
|
|
||||||
if (useLoadedOrder)
|
|
||||||
{
|
{
|
||||||
playbackOrder = new List<int>(m_saveData.savedPlaybackOrder);
|
if (barkManagerEntryIndices[i] < 0 || barkManagerEntryIndices[i] >= barkManager.m_barks.Length)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
playbackOrder = Enumerable.Range(0, barkCount).ToList();
|
|
||||||
if (shuffleOrder)
|
|
||||||
{
|
{
|
||||||
for (int i = playbackOrder.Count - 1; i > 0; i--) {
|
Debug.LogError($"BarkPlayer ({gameObject.name}): Invalid Bark Manager Entry Index {barkManagerEntryIndices[i]} at array position {i}. Max index is {barkManager.m_barks.Length - 1}. Disabling.", this);
|
||||||
int j = Random.Range(0, i + 1);
|
enabled = false;
|
||||||
(playbackOrder[i], playbackOrder[j]) = (playbackOrder[j], playbackOrder[i]);
|
return;
|
||||||
}
|
|
||||||
m_saveData.wasShuffled = true;
|
|
||||||
} else {
|
|
||||||
m_saveData.wasShuffled = false;
|
|
||||||
}
|
}
|
||||||
m_saveData.savedPlaybackOrder = new List<int>(playbackOrder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nextBarkSequenceIndex = Mathf.Clamp(m_saveData.savedNextBarkSequenceIndex, 0, playbackOrder.Count);
|
isInitialized = true;
|
||||||
|
|
||||||
if (nextBarkSequenceIndex >= playbackOrder.Count && !loopWhileInside)
|
// Check if we should start playing immediately after initialization
|
||||||
{
|
// (e.g., if loaded state is 'started' and player is already inside trigger)
|
||||||
Debug.Log($"BarkPlayer on {gameObject.name}: Sequence for entry {barkManagerEntryIndex} already completed (and not looping).", this);
|
CheckForAutoStartIfInside();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnDrawGizmos()
|
||||||
|
{
|
||||||
|
Collider col = GetComponent<Collider>();
|
||||||
|
if (col != null)
|
||||||
|
{
|
||||||
|
Gizmos.color = isTriggeringObjectInside ? new Color(1f, 0.5f, 0.5f, 0.4f) : new Color(0.5f, 1f, 0.5f, 0.3f); // Red when active
|
||||||
|
if (col is BoxCollider box) Gizmos.DrawCube(transform.TransformPoint(box.center), Vector3.Scale(box.size, transform.lossyScale));
|
||||||
|
else if (col is SphereCollider sphere) Gizmos.DrawSphere(transform.TransformPoint(sphere.center), sphere.radius * MaxComponent(transform.lossyScale));
|
||||||
|
// Add other collider types if needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Trigger Handling
|
||||||
|
|
||||||
private void OnTriggerEnter(Collider other)
|
private void OnTriggerEnter(Collider other)
|
||||||
{
|
{
|
||||||
if (!isInitialized) return;
|
// Ignore if not initialized, not enabled, or runtime state is not 'Started'
|
||||||
|
if (!isInitialized || !enabled || !_runtime_IsStarted) return;
|
||||||
|
|
||||||
// --- Check Layer Mask ---
|
// --- Layer Check ---
|
||||||
int otherLayer = other.gameObject.layer;
|
int otherLayer = other.gameObject.layer;
|
||||||
// Check if the entering object's layer is in our mask
|
|
||||||
if ((triggeringLayers.value & (1 << otherLayer)) == 0)
|
if ((triggeringLayers.value & (1 << otherLayer)) == 0)
|
||||||
{
|
{
|
||||||
// Layer not in mask, ignore this trigger event
|
return; // Layer not in mask, ignore this trigger event
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// --- End Layer Check ---
|
|
||||||
|
|
||||||
// Object on a triggering layer entered
|
// --- Check if already processing an object ---
|
||||||
|
// This prevents multiple triggering objects from interfering. Only the first one in matters.
|
||||||
|
if (isTriggeringObjectInside) return;
|
||||||
|
|
||||||
|
// --- Valid Trigger ---
|
||||||
Debug.Log($"BarkPlayer ({gameObject.name}): Triggering object '{other.name}' entered.", this);
|
Debug.Log($"BarkPlayer ({gameObject.name}): Triggering object '{other.name}' entered.", this);
|
||||||
isTriggeringObjectInside = true;
|
isTriggeringObjectInside = true;
|
||||||
currentTriggererTransform = other.transform; // Store the transform for barking
|
currentTriggererTransform = other.transform; // Store the transform for barking
|
||||||
|
|
||||||
// Start playback if not already running
|
// --- Start Playback Coroutine if not already running ---
|
||||||
if (currentPlaybackCoroutine == null)
|
if (currentPlaybackCoroutine == null)
|
||||||
{
|
{
|
||||||
|
// Apply potentially loaded/saved array index before starting loop
|
||||||
|
currentEntryArrayIndex = Mathf.Clamp(m_saveData.savedCurrentEntryArrayIndex, 0, Mathf.Max(0, barkManagerEntryIndices.Length - 1));
|
||||||
|
// nextBarkSequenceIndex will be set by PrepareEntrySequence inside the coroutine
|
||||||
currentPlaybackCoroutine = StartCoroutine(ContinuousPlaybackLoop());
|
currentPlaybackCoroutine = StartCoroutine(ContinuousPlaybackLoop());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTriggerExit(Collider other)
|
private void OnTriggerExit(Collider other)
|
||||||
{
|
{
|
||||||
if (!isInitialized) return;
|
if (!isInitialized || !enabled) return;
|
||||||
|
|
||||||
// --- Check Layer Mask ---
|
// --- Layer Check ---
|
||||||
int otherLayer = other.gameObject.layer;
|
int otherLayer = other.gameObject.layer;
|
||||||
if ((triggeringLayers.value & (1 << otherLayer)) == 0)
|
if ((triggeringLayers.value & (1 << otherLayer)) == 0)
|
||||||
{
|
{
|
||||||
// Ignore exit events from non-triggering layers
|
return; // Ignore exit events from non-triggering layers
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
// --- End Layer Check ---
|
|
||||||
|
|
||||||
// Object on a triggering layer exited
|
// --- Check if the *tracked* object is the one exiting ---
|
||||||
// Important: Only react if the exiting object is the *same one* we stored.
|
|
||||||
// This handles cases where multiple triggering objects might enter/exit.
|
|
||||||
if (other.transform == currentTriggererTransform)
|
if (other.transform == currentTriggererTransform)
|
||||||
{
|
{
|
||||||
Debug.Log($"BarkPlayer ({gameObject.name}): Triggering object '{other.name}' exited.", this);
|
Debug.Log($"BarkPlayer ({gameObject.name}): Triggering object '{other.name}' exited.", this);
|
||||||
isTriggeringObjectInside = false;
|
isTriggeringObjectInside = false;
|
||||||
currentTriggererTransform = null; // Clear the stored transform
|
currentTriggererTransform = null; // Clear the stored transform
|
||||||
|
|
||||||
// Stop the playback coroutine
|
// --- Stop the Playback Coroutine ---
|
||||||
if (currentPlaybackCoroutine != null)
|
if (currentPlaybackCoroutine != null)
|
||||||
{
|
{
|
||||||
StopCoroutine(currentPlaybackCoroutine);
|
StopCoroutine(currentPlaybackCoroutine);
|
||||||
currentPlaybackCoroutine = null;
|
currentPlaybackCoroutine = null;
|
||||||
Debug.Log($"BarkPlayer ({gameObject.name}): Stopping playback loop due to exit.", this);
|
Debug.Log($"BarkPlayer ({gameObject.name}): Stopping playback loop due to exit.", this);
|
||||||
// Optional: Stop BarkManager audio immediately
|
// Optional: Immediately stop BarkManager audio?
|
||||||
// if (barkManager != null && barkManager.IsPlaying) barkManager.m_audioSource.Stop();
|
// if (barkManager != null && barkManager.IsPlaying) barkManager.m_audioSource.Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Else: Some other object on a triggering layer exited, but the primary one is still inside. Do nothing.
|
// Else: Some other object on a triggering layer exited, but the primary one is still inside. Do nothing.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContinuousPlaybackLoop now uses the stored currentTriggererTransform
|
#endregion
|
||||||
|
|
||||||
|
#region Playback Logic
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets up the playbackOrder list for a specific BarkManager entry index.
|
||||||
|
/// Resets or applies saved nextBarkSequenceIndex.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entryIndexToPrepare">The index within BarkManager's m_barks array.</param>
|
||||||
|
/// <returns>True if preparation was successful (entry has barks), false otherwise.</returns>
|
||||||
|
private bool PrepareEntrySequence(int entryIndexToPrepare)
|
||||||
|
{
|
||||||
|
if (barkManager == null) return false;
|
||||||
|
|
||||||
|
int barkCount = barkManager.GetBarkCountInEntry(entryIndexToPrepare);
|
||||||
|
if (barkCount <= 0)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"BarkPlayer ({gameObject.name}): BarkEntry {entryIndexToPrepare} (from array index {currentEntryArrayIndex}) has no barks. Skipping entry.", this);
|
||||||
|
playbackOrder = new List<int>(); // Ensure list is empty
|
||||||
|
nextBarkSequenceIndex = 0;
|
||||||
|
return false; // Indicate nothing to play for this entry
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate sequence order (sequential or shuffled)
|
||||||
|
playbackOrder = Enumerable.Range(0, barkCount).ToList();
|
||||||
|
if (shuffleOrder)
|
||||||
|
{
|
||||||
|
// Simple Fisher-Yates shuffle
|
||||||
|
for (int i = playbackOrder.Count - 1; i > 0; i--)
|
||||||
|
{
|
||||||
|
int j = Random.Range(0, i + 1);
|
||||||
|
// Tuple swap is concise
|
||||||
|
(playbackOrder[i], playbackOrder[j]) = (playbackOrder[j], playbackOrder[i]);
|
||||||
|
}
|
||||||
|
// Debug.Log($"BarkPlayer ({gameObject.name}): Shuffled order for entry {entryIndexToPrepare}.", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset sequence progress. Apply saved index ONLY if the loaded array index matches the current one.
|
||||||
|
if (currentEntryArrayIndex == m_saveData.savedCurrentEntryArrayIndex)
|
||||||
|
{
|
||||||
|
nextBarkSequenceIndex = Mathf.Clamp(m_saveData.savedNextBarkSequenceIndex, 0, playbackOrder.Count);
|
||||||
|
// Debug.Log($"BarkPlayer ({gameObject.name}): Resuming entry {entryIndexToPrepare} at bark index {nextBarkSequenceIndex}.", this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextBarkSequenceIndex = 0; // Start new entry from beginning if array index differs from saved
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // Preparation successful
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Main coroutine managing the playback loop through entries and barks.
|
||||||
|
/// </summary>
|
||||||
private IEnumerator ContinuousPlaybackLoop()
|
private IEnumerator ContinuousPlaybackLoop()
|
||||||
{
|
{
|
||||||
Debug.Log($"BarkPlayer ({gameObject.name}): Starting playback loop.", this);
|
Debug.Log($"BarkPlayer ({gameObject.name}): Starting playback loop.", this);
|
||||||
|
|
||||||
// Ensure we have a target transform before proceeding
|
// Initial check for valid configuration
|
||||||
while (currentTriggererTransform == null && isTriggeringObjectInside)
|
if (barkManagerEntryIndices == null || barkManagerEntryIndices.Length == 0)
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"BarkPlayer ({gameObject.name}): Waiting for valid triggerer transform.", this);
|
Debug.LogError($"BarkPlayer ({gameObject.name}): Cannot start loop, entry indices array is empty or null.", this);
|
||||||
yield return null; // Wait a frame if trigger happened but transform wasn't stored yet (unlikely but safe)
|
currentPlaybackCoroutine = null; // Ensure coroutine reference is cleared
|
||||||
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main loop runs while a triggering object is inside AND we have its transform
|
// Outer loop: Continues as long as a valid triggering object is inside
|
||||||
while (isTriggeringObjectInside && currentTriggererTransform != null)
|
while (isTriggeringObjectInside && currentTriggererTransform != null)
|
||||||
{
|
{
|
||||||
if (playbackOrder == null || playbackOrder.Count == 0)
|
// --- Step 1: Prepare for Current Entry ---
|
||||||
|
// Safety check/loop wrap for the array index
|
||||||
|
if (currentEntryArrayIndex >= barkManagerEntryIndices.Length)
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"BarkPlayer ({gameObject.name}): No barks to play in entry {barkManagerEntryIndex}.", this);
|
currentEntryArrayIndex = 0;
|
||||||
yield break;
|
if (barkManagerEntryIndices.Length == 0)
|
||||||
|
{ // Should be caught earlier, but double-check
|
||||||
|
Debug.LogError($"BarkPlayer ({gameObject.name}): Entry indices array became empty during loop?", this);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sequenceFinished = nextBarkSequenceIndex >= playbackOrder.Count;
|
int currentManagerEntryIndex = barkManagerEntryIndices[currentEntryArrayIndex];
|
||||||
|
// Debug.Log($"BarkPlayer ({gameObject.name}): Preparing Entry Index: {currentManagerEntryIndex} (Array Pos: {currentEntryArrayIndex}).", this);
|
||||||
|
|
||||||
if (sequenceFinished)
|
// Prepare the sequence (generate playbackOrder, set nextBarkSequenceIndex)
|
||||||
|
bool canPlayEntry = PrepareEntrySequence(currentManagerEntryIndex);
|
||||||
|
|
||||||
|
if (!canPlayEntry) // Skip this entry if it has no barks
|
||||||
{
|
{
|
||||||
if (loopWhileInside)
|
// Debug.Log($"BarkPlayer ({gameObject.name}): Skipping empty BarkEntry {currentManagerEntryIndex}.", this);
|
||||||
|
currentEntryArrayIndex++; // Move immediately to the next entry index
|
||||||
|
yield return null; // Wait a frame before checking the next entry in the outer loop
|
||||||
|
continue; // Go to next iteration of the outer while loop (skips delays below)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Step 2: Inner Loop - Play Barks within the Current Entry ---
|
||||||
|
while (nextBarkSequenceIndex < playbackOrder.Count && isTriggeringObjectInside && currentTriggererTransform != null)
|
||||||
|
{
|
||||||
|
// Wait for BarkManager's AudioSource if it's busy
|
||||||
|
while (barkManager.IsPlaying && isTriggeringObjectInside)
|
||||||
{
|
{
|
||||||
Debug.Log($"BarkPlayer ({gameObject.name}): Sequence finished, looping.", this);
|
yield return null; // Wait a frame
|
||||||
nextBarkSequenceIndex = 0;
|
}
|
||||||
if (shuffleOrder) {
|
// Re-check condition after waiting, player might have left
|
||||||
InitializePlaybackOrder();
|
if (!isTriggeringObjectInside || currentTriggererTransform == null) break;
|
||||||
nextBarkSequenceIndex = 0;
|
|
||||||
if (playbackOrder.Count == 0) yield break;
|
|
||||||
|
// Play the specific bark for this step in the sequence
|
||||||
|
int barkIndexToPlay = playbackOrder[nextBarkSequenceIndex];
|
||||||
|
Debug.Log($"BarkPlayer ({gameObject.name}): Playing Bark {nextBarkSequenceIndex + 1}/{playbackOrder.Count} (Actual Index: {barkIndexToPlay}) from Entry {currentManagerEntryIndex}.", this);
|
||||||
|
|
||||||
|
// Play using the specific index, passing the triggerer's transform
|
||||||
|
AudioClip playedClip = barkManager.PlayBark(currentManagerEntryIndex, currentTriggererTransform, barkIndexToPlay);
|
||||||
|
|
||||||
|
nextBarkSequenceIndex++; // Increment progress within this entry's sequence
|
||||||
|
|
||||||
|
// Wait for the bark's duration plus the inter-bark delay
|
||||||
|
float waitTime = delayBetweenBarks;
|
||||||
|
if (playedClip != null) { waitTime += Mathf.Max(0f, playedClip.length); } // Ensure non-negative length
|
||||||
|
|
||||||
|
// Perform the wait, checking continuously if the player leaves
|
||||||
|
if (waitTime > 0)
|
||||||
|
{
|
||||||
|
float timer = 0f;
|
||||||
|
while (timer < waitTime && isTriggeringObjectInside && currentTriggererTransform != null)
|
||||||
|
{
|
||||||
|
timer += Time.deltaTime;
|
||||||
|
yield return null; // Wait one frame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.Log($"BarkPlayer ({gameObject.name}): Sequence finished, not looping. Waiting for exit.", this);
|
// Minimum one frame wait even if no delay/clip length
|
||||||
// Wait until trigger object leaves
|
yield return null;
|
||||||
while(isTriggeringObjectInside && currentTriggererTransform != null) { yield return null; }
|
|
||||||
yield break; // Exit coroutine
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If player left during wait, break inner loop
|
||||||
|
if (!isTriggeringObjectInside || currentTriggererTransform == null) break;
|
||||||
|
|
||||||
|
} // --- End of Inner Loop (Barks within Entry) ---
|
||||||
|
|
||||||
|
|
||||||
|
// --- Step 3: Transition to Next Entry (if player didn't exit) ---
|
||||||
|
if (isTriggeringObjectInside && currentTriggererTransform != null)
|
||||||
|
{
|
||||||
|
// Current entry sequence finished normally
|
||||||
|
Debug.Log($"BarkPlayer ({gameObject.name}): Finished sequence for Entry {currentManagerEntryIndex}.", this);
|
||||||
|
|
||||||
|
currentEntryArrayIndex++; // Move to the next entry index in the array
|
||||||
|
|
||||||
|
// Check for wrapping / end of all entries in the array
|
||||||
|
if (currentEntryArrayIndex >= barkManagerEntryIndices.Length)
|
||||||
|
{
|
||||||
|
Debug.Log($"BarkPlayer ({gameObject.name}): Finished all entries in the array. Looping back.", this);
|
||||||
|
currentEntryArrayIndex = 0; // Loop back to the start of the array
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the specified delay BETWEEN entries
|
||||||
|
if (delayBetweenEntries > 0)
|
||||||
|
{
|
||||||
|
Debug.Log($"BarkPlayer ({gameObject.name}): Waiting {delayBetweenEntries}s before next entry.", this);
|
||||||
|
float timer = 0f;
|
||||||
|
while (timer < delayBetweenEntries && isTriggeringObjectInside && currentTriggererTransform != null)
|
||||||
|
{
|
||||||
|
timer += Time.deltaTime;
|
||||||
|
yield return null; // Wait one frame
|
||||||
|
}
|
||||||
|
// If player left during delay, break outer loop
|
||||||
|
if (!isTriggeringObjectInside || currentTriggererTransform == null) break;
|
||||||
|
}
|
||||||
|
// The outer loop will then continue, preparing the next entry
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for BarkManager's AudioSource if necessary
|
} // --- End of Outer Loop (isTriggeringObjectInside) ---
|
||||||
if (barkManager.IsPlaying)
|
|
||||||
{
|
|
||||||
yield return null;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Play the bark using the stored transform
|
Debug.Log($"BarkPlayer ({gameObject.name}): Playback loop finished (Triggering object left or component stopped).", this);
|
||||||
int barkIndexToPlay = playbackOrder[nextBarkSequenceIndex];
|
// Clear coroutine reference *if* this coroutine instance is the one finishing
|
||||||
Debug.Log($"BarkPlayer ({gameObject.name}): Playing bark {nextBarkSequenceIndex} (actual index: {barkIndexToPlay}) targeting '{currentTriggererTransform.name}'.", this);
|
if (currentPlaybackCoroutine != null && (!isTriggeringObjectInside || currentTriggererTransform == null))
|
||||||
|
{
|
||||||
// Pass the stored transform
|
currentPlaybackCoroutine = null;
|
||||||
AudioClip playedClip = barkManager.PlayBark(barkManagerEntryIndex, currentTriggererTransform, barkIndexToPlay);
|
|
||||||
|
|
||||||
nextBarkSequenceIndex++;
|
|
||||||
|
|
||||||
float waitTime = delayBetweenBarks;
|
|
||||||
if (playedClip != null) { waitTime += Mathf.Max(0f, playedClip.length); }
|
|
||||||
|
|
||||||
if (waitTime > 0) { yield return new WaitForSeconds(waitTime); }
|
|
||||||
else { yield return null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.Log($"BarkPlayer ({gameObject.name}): Playback loop finished (Triggering object left or transform lost).", this);
|
|
||||||
// Reset coroutine reference *if* it was this instance that finished it
|
|
||||||
// (OnTriggerExit might have already cleared it)
|
|
||||||
if (currentPlaybackCoroutine != null && !isTriggeringObjectInside) {
|
|
||||||
currentPlaybackCoroutine = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
// --- Save System Integration --- (Remains the same)
|
#region Public Control Methods
|
||||||
public override string RecordData() { /* ... same as before ... */
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables the BarkPlayer's runtime operation. It will start playing sequences when triggered.
|
||||||
|
/// If a triggering object is already inside, playback may start immediately.
|
||||||
|
/// </summary>
|
||||||
|
[Button] // Example for Odin Inspector button
|
||||||
|
public void StartPlayer()
|
||||||
|
{
|
||||||
|
if (!_runtime_IsStarted)
|
||||||
|
{
|
||||||
|
Debug.Log($"BarkPlayer ({gameObject.name}): Player Runtime Started.", this);
|
||||||
|
_runtime_IsStarted = true;
|
||||||
|
// If already initialized, check if we need to immediately start playback
|
||||||
|
if (isInitialized)
|
||||||
|
{
|
||||||
|
CheckForAutoStartIfInside();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disables the BarkPlayer's runtime operation. Stops any current playback and ignores future triggers.
|
||||||
|
/// </summary>
|
||||||
|
[Button] // Example for Odin Inspector button
|
||||||
|
public void StopPlayer()
|
||||||
|
{
|
||||||
|
if (_runtime_IsStarted)
|
||||||
|
{
|
||||||
|
Debug.Log($"BarkPlayer ({gameObject.name}): Player Runtime Stopped.", this);
|
||||||
|
_runtime_IsStarted = false;
|
||||||
|
|
||||||
|
// Stop any currently running playback coroutine
|
||||||
|
if (currentPlaybackCoroutine != null)
|
||||||
|
{
|
||||||
|
StopCoroutine(currentPlaybackCoroutine);
|
||||||
|
currentPlaybackCoroutine = null; // Clear the reference
|
||||||
|
Debug.Log($"BarkPlayer ({gameObject.name}): Stopping active playback loop.", this);
|
||||||
|
// Optional: Immediately stop BarkManager audio?
|
||||||
|
// if (barkManager != null && barkManager.IsPlaying) barkManager.m_audioSource.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the player is currently inside the trigger and starts the playback loop
|
||||||
|
/// if the BarkPlayer is started (_runtime_IsStarted=true) but the loop isn't currently running.
|
||||||
|
/// Useful after initialization (in Start) or after calling StartPlayer manually.
|
||||||
|
/// </summary>
|
||||||
|
private void CheckForAutoStartIfInside()
|
||||||
|
{
|
||||||
|
if (_runtime_IsStarted && isTriggeringObjectInside && currentTriggererTransform != null && currentPlaybackCoroutine == null && isInitialized)
|
||||||
|
{
|
||||||
|
Debug.Log($"BarkPlayer ({gameObject.name}): Triggering object already inside and player is started. Starting playback loop.", this);
|
||||||
|
// Ensure indices are correctly set from save data before starting
|
||||||
|
currentEntryArrayIndex = Mathf.Clamp(m_saveData.savedCurrentEntryArrayIndex, 0, Mathf.Max(0, barkManagerEntryIndices.Length - 1));
|
||||||
|
currentPlaybackCoroutine = StartCoroutine(ContinuousPlaybackLoop());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Save System Integration (Pixel Crushers Saver)
|
||||||
|
|
||||||
|
public override string RecordData()
|
||||||
|
{
|
||||||
|
// Update save data container with current runtime state
|
||||||
|
m_saveData.savedIsStarted = this._runtime_IsStarted;
|
||||||
|
m_saveData.savedCurrentEntryArrayIndex = this.currentEntryArrayIndex;
|
||||||
m_saveData.savedNextBarkSequenceIndex = this.nextBarkSequenceIndex;
|
m_saveData.savedNextBarkSequenceIndex = this.nextBarkSequenceIndex;
|
||||||
m_saveData.savedPlaybackOrder = new List<int>(this.playbackOrder ?? new List<int>());
|
m_saveData.wasShuffled = this.shuffleOrder; // Record shuffle setting at time of save
|
||||||
m_saveData.wasShuffled = this.shuffleOrder;
|
|
||||||
|
// Serialize the save data container
|
||||||
return SaveSystem.Serialize(m_saveData);
|
return SaveSystem.Serialize(m_saveData);
|
||||||
}
|
}
|
||||||
public override void ApplyData(string s) { /* ... same as before ... */
|
|
||||||
if (string.IsNullOrEmpty(s)) return;
|
public override void ApplyData(string s)
|
||||||
var loadedData = SaveSystem.Deserialize<BarkPlayerData>(s);
|
{
|
||||||
if (loadedData != null) { m_saveData = loadedData; }
|
// Handle case where there is no save data for this component
|
||||||
else Debug.LogError($"BarkPlayer ({gameObject.name}): Failed to deserialize save data.", this);
|
if (string.IsNullOrEmpty(s))
|
||||||
|
{
|
||||||
|
// No save data - runtime state keeps the value set by startAutomaticallyOnLoad in Awake
|
||||||
|
_runtime_IsStarted = startAutomaticallyOnLoad;
|
||||||
|
// Reset progress indices if no save data
|
||||||
|
m_saveData.savedCurrentEntryArrayIndex = 0;
|
||||||
|
m_saveData.savedNextBarkSequenceIndex = 0;
|
||||||
|
Debug.Log($"BarkPlayer ({gameObject.name}): No save data found, using startAutomaticallyOnLoad ({startAutomaticallyOnLoad}).", this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the saved data string
|
||||||
|
var loadedData = SaveSystem.Deserialize<BarkPlayerData>(s);
|
||||||
|
if (loadedData != null)
|
||||||
|
{
|
||||||
|
m_saveData = loadedData; // Store the loaded data
|
||||||
|
// Apply the loaded runtime state
|
||||||
|
this._runtime_IsStarted = m_saveData.savedIsStarted;
|
||||||
|
// Indices will be applied in Start() / PrepareEntrySequence() using m_saveData
|
||||||
|
Debug.Log($"BarkPlayer ({gameObject.name}): Applied loaded data. Started={_runtime_IsStarted}, EntryIdx={m_saveData.savedCurrentEntryArrayIndex}, BarkIdx={m_saveData.savedNextBarkSequenceIndex}.", this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError($"BarkPlayer ({gameObject.name}): Failed to deserialize save data. Using defaults.", this);
|
||||||
|
// Fallback: use inspector setting if deserialize failed
|
||||||
|
this._runtime_IsStarted = startAutomaticallyOnLoad;
|
||||||
|
m_saveData.savedCurrentEntryArrayIndex = 0;
|
||||||
|
m_saveData.savedNextBarkSequenceIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// After applying data, ensure consistency: if loaded state is 'stopped', stop coroutine
|
||||||
|
if (!this._runtime_IsStarted && currentPlaybackCoroutine != null)
|
||||||
|
{
|
||||||
|
StopCoroutine(currentPlaybackCoroutine);
|
||||||
|
currentPlaybackCoroutine = null;
|
||||||
|
}
|
||||||
|
// The CheckForAutoStartIfInside() call in Start() will handle restarting the
|
||||||
|
// coroutine if the loaded state is 'started' and the player is inside the trigger.
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Gizmos --- (Remain the same, uses isTriggeringObjectInside now)
|
#endregion
|
||||||
private void OnDrawGizmos() {
|
|
||||||
Collider col = GetComponent<Collider>();
|
#region Helper Methods
|
||||||
if (col != null) {
|
|
||||||
Gizmos.color = isTriggeringObjectInside ? new Color(1f, 0.5f, 0.5f, 0.4f) : new Color(0.5f, 1f, 0.5f, 0.3f);
|
/// <summary> Helper to find the largest component of a Vector3 (for Gizmos). </summary>
|
||||||
if (col is BoxCollider box) Gizmos.DrawCube(transform.TransformPoint(box.center), Vector3.Scale(box.size, transform.lossyScale));
|
|
||||||
else if (col is SphereCollider sphere) Gizmos.DrawSphere(transform.TransformPoint(sphere.center), sphere.radius * MaxComponent(transform.lossyScale));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private float MaxComponent(Vector3 v) => Mathf.Max(Mathf.Max(v.x, v.y), v.z);
|
private float MaxComponent(Vector3 v) => Mathf.Max(Mathf.Max(v.x, v.y), v.z);
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user