Improved UI textbox
This commit is contained in:
@@ -1,55 +1,73 @@
|
||||
class_name DialogueAction
|
||||
|
||||
# Runs a .dialogue file through the VNTextbox and returns CUTSCENE_CONTINUE when
|
||||
# the last line is dismissed. Mutations in the .dialogue file are executed
|
||||
# automatically by DialogueManager before the line is returned.
|
||||
#
|
||||
# extra_game_states: additional objects/dicts whose properties and methods are
|
||||
# accessible inside the .dialogue file (alongside all autoloads).
|
||||
const _TextboxGd = preload("res://ui/component/DialogueTextbox.gd")
|
||||
const _ChoiceBoxGd = preload("res://ui/component/DialogueChoiceBox.gd")
|
||||
|
||||
enum DialogueMode {
|
||||
CONVERSATION, # blocks movement, player advances
|
||||
NARRATION, # non-blocking, player advances
|
||||
AMBIENT, # non-blocking, timed auto-advance
|
||||
}
|
||||
|
||||
static func dialogueCallable(params:Dictionary) -> int:
|
||||
assert(params.has('resource'))
|
||||
var resource:DialogueResource = params['resource']
|
||||
assert(params.has('basePath'))
|
||||
var basePath:String = params['basePath']
|
||||
var title:String = params.get('title', 'start')
|
||||
var extraStates:Array = params.get('extraStates', [])
|
||||
var characterTargets:Dictionary = params.get('characterTargets', {})
|
||||
var mode:DialogueMode = params.get('mode', DialogueMode.CONVERSATION)
|
||||
|
||||
var resource:DialogueResource = _loadLocaleResource(basePath)
|
||||
assert(resource != null, "DialogueAction: could not load resource for path: " + basePath)
|
||||
|
||||
if mode == DialogueMode.CONVERSATION:
|
||||
UI.activeConversation = true
|
||||
|
||||
var advancementMode:int = (
|
||||
_TextboxGd.AdvancementMode.TIMED
|
||||
if mode == DialogueMode.AMBIENT
|
||||
else _TextboxGd.AdvancementMode.PLAYER
|
||||
)
|
||||
|
||||
UI.dialogueActive = true
|
||||
var line:DialogueLine = await DialogueManager.get_next_dialogue_line(resource, title, extraStates)
|
||||
while line != null:
|
||||
var target:Node3D = _getLineTarget(line.character, characterTargets)
|
||||
var text:String = line.text
|
||||
var entity:Entity = OVERWORLD.getEntityByDialogueName(line.character)
|
||||
|
||||
if line.responses.size() > 0:
|
||||
# Show text then auto-pick the first allowed response.
|
||||
# Replace this block with a real response UI when branching dialogue is needed.
|
||||
await UI.MAIN_CHATBOX.setTextAndWait(text, target)
|
||||
var nextId:String = ""
|
||||
for response:DialogueResponse in line.responses:
|
||||
if response.is_allowed:
|
||||
nextId = response.next_id
|
||||
break
|
||||
line = await DialogueManager.get_next_dialogue_line(resource, nextId, extraStates)
|
||||
var textbox = _TextboxGd.SCENE.instantiate()
|
||||
UI.chatBoxContainer.add_child(textbox)
|
||||
textbox.setup(line, entity, advancementMode)
|
||||
await textbox.dismissed
|
||||
|
||||
var allowedResponses:Array = line.responses.filter(func(r): return r.is_allowed)
|
||||
if allowedResponses.size() > 0:
|
||||
var playerEntity:Entity = OVERWORLD.getPlayerEntity()
|
||||
var choiceBox = _ChoiceBoxGd.SCENE.instantiate()
|
||||
UI.chatBoxContainer.add_child(choiceBox)
|
||||
choiceBox.setup(line.responses, playerEntity)
|
||||
var chosen:DialogueResponse = await choiceBox.chosen
|
||||
line = await DialogueManager.get_next_dialogue_line(resource, chosen.next_id, extraStates)
|
||||
else:
|
||||
await UI.MAIN_CHATBOX.setTextAndWait(text, target)
|
||||
line = await DialogueManager.get_next_dialogue_line(resource, line.next_id, extraStates)
|
||||
|
||||
UI.dialogueActive = false
|
||||
if mode == DialogueMode.CONVERSATION:
|
||||
UI.activeConversation = false
|
||||
|
||||
return Cutscene.CUTSCENE_CONTINUE
|
||||
|
||||
# "player" (case-insensitive) maps to the player entity; any other named
|
||||
# character maps to the npc entity; empty character (narrator) uses no target.
|
||||
static func _getLineTarget(character:String, characterTargets:Dictionary) -> Node3D:
|
||||
if characterTargets.is_empty() or character == "":
|
||||
return null
|
||||
if character.to_lower() == "player":
|
||||
return characterTargets.get("player", null)
|
||||
return characterTargets.get("npc", null)
|
||||
static func _loadLocaleResource(basePath:String) -> DialogueResource:
|
||||
var lang:String = TranslationServer.get_locale().left(2)
|
||||
var localePath:String = basePath + "." + lang + ".dialogue"
|
||||
if ResourceLoader.exists(localePath):
|
||||
return load(localePath)
|
||||
var fallback:String = basePath + ".en.dialogue"
|
||||
if ResourceLoader.exists(fallback):
|
||||
return load(fallback)
|
||||
return null
|
||||
|
||||
static func getDialogueCallable(resource:DialogueResource, title:String = 'start', extraStates:Array = [], characterTargets:Dictionary = {}) -> Dictionary:
|
||||
static func getDialogueCallable(basePath:String, title:String = "start", extraStates:Array = [], mode:DialogueMode = DialogueMode.CONVERSATION) -> Dictionary:
|
||||
return {
|
||||
'function': dialogueCallable,
|
||||
'resource': resource,
|
||||
'title': title,
|
||||
'extraStates': extraStates,
|
||||
'characterTargets': characterTargets,
|
||||
"function": dialogueCallable,
|
||||
"basePath": basePath,
|
||||
"title": title,
|
||||
"extraStates": extraStates,
|
||||
"mode": mode,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user