-
Notifications
You must be signed in to change notification settings - Fork 1
/
LoveDialogueParser.lua
254 lines (215 loc) · 9.19 KB
/
LoveDialogueParser.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
local Parser = {}
local CallbackHandler = require "CallbackHandler"
local PortraitManager = require "PortraitManager"
local function loadLuaFile(filePath)
local chunk, err = loadfile(filePath)
if not chunk then
print("Error loading file:", err)
return nil
end
return chunk()
end
local function parseTextWithTags(text)
local parsedText = ""
local effectsTable = {}
local openEffects = {}
local currentIndex = 1
while currentIndex <= #text do
local startTag, endTag, tag, content = text:find("{([^:}]+):([^}]*)}", currentIndex)
local closingStartTag, closingEndTag, closingTag = text:find("{/([^}]+)}", currentIndex)
if not startTag and not closingStartTag then
parsedText = parsedText .. text:sub(currentIndex)
break
end
if closingStartTag and (not startTag or closingStartTag < startTag) then
parsedText = parsedText .. text:sub(currentIndex, closingStartTag - 1)
local effect
for i = #openEffects, 1, -1 do
if openEffects[i].type == closingTag then
effect = table.remove(openEffects, i)
break
end
end
if effect then
effect.endIndex = #parsedText
table.insert(effectsTable, effect)
end
currentIndex = closingEndTag + 1
else
parsedText = parsedText .. text:sub(currentIndex, startTag - 1)
table.insert(openEffects, {type = tag, content = content, startIndex = #parsedText + 1})
currentIndex = endTag + 1
end
end
for _, effect in ipairs(openEffects) do
effect.endIndex = #parsedText
table.insert(effectsTable, effect)
end
return parsedText, effectsTable
end
Parser.parseTextWithTags = parseTextWithTags
function Parser.parseFile(filePath)
local lines = {}
local characters = {}
local currentLine = 1
local scenes = {}
local currentScene = "default"
local callbacks = {}
-- Read file content
local fileContent = love.filesystem.read(filePath)
if not fileContent then
error("Could not read file: " .. filePath)
return
end
-- First pass: Handle portrait definitions
for line in fileContent:gmatch("[^\r\n]+") do
local character, path = line:match("^@portrait%s+(%S+)%s+(.+)$")
if character and path then
PortraitManager.loadPortrait(character, path:match("^%s*(.-)%s*$"))
end
end
-- Second pass: collect all callbacks
for line in fileContent:gmatch("[^\r\n]+") do
if line:match("^@callback%s+") then
local name, code = line:match("^@callback%s+(%w+)%s+(.+)$")
if name and code then
local env = setmetatable({}, {__index = _G})
local fn, err = load(code, "callback_" .. name, "t", env)
if fn then
local success, result = pcall(fn)
if success and type(result) == "function" then
callbacks[name] = result
print("Successfully loaded callback: " .. name)
else
print("Error executing callback " .. name .. ": " .. tostring(result))
end
else
print("Error loading callback " .. name .. ": " .. err)
end
end
end
end
-- Third pass: parse dialogue and choices
local fileLines = {}
for line in fileContent:gmatch("[^\r\n]+") do
if not line:match("^@callback") then
table.insert(fileLines, line)
end
end
for _, line in ipairs(fileLines) do
local character, text = line:match("^(%S+):%s*(.+)$")
if character and text then
local isEnd = text:match("%(end%)$")
if isEnd then
text = text:gsub("%s*%(end%)$", "")
end
local parsedText, effects = Parser.parseTextWithTags(text)
local parsedLine = {
character = character,
text = parsedText,
isEnd = isEnd,
effects = effects,
choices = {}
}
lines[currentLine] = parsedLine
if not characters[character] then
characters[character] = {r = love.math.random(), g = love.math.random(), b = love.math.random()}
end
currentLine = currentLine + 1
elseif line:match("^%->") then
print("DEBUG: Processing choice line:", line)
line = line:gsub("[\r\n]", "")
local choiceText, target, callbackName = line:match("^%->%s*([^%[]+)%s*%[target:([%w_]+)%]%s*@?(%w*)%s*$")
print("DEBUG: Raw matches:")
print(" Text:", choiceText and '"'..choiceText..'"' or "nil")
print(" Target:", target and '"'..target..'"' or "nil")
print(" Callback:", callbackName and '"'..callbackName..'"' or "nil")
if choiceText then
-- Trim whitespace
choiceText = choiceText:match("^%s*(.-)%s*$")
target = target and target:match("^%s*(.-)%s*$")
if callbackName then
callbackName = callbackName:match("^%s*(.-)%s*$")
end
print("DEBUG: After trimming:")
print(" Text:", '"'..choiceText..'"')
print(" Target:", target and '"'..target..'"' or "nil")
print(" Callback:", callbackName and '"'..callbackName..'"' or "nil")
local parsedChoiceText, choiceEffects = Parser.parseTextWithTags(choiceText)
local callback = nil
if callbackName and callbackName ~= "" then
print("DEBUG: Looking up callback:", callbackName)
callback = CallbackHandler.getCallback(callbackName)
print("DEBUG: Callback found:", callback ~= nil)
end
local choice = {
text = choiceText,
parsedText = parsedChoiceText,
effects = choiceEffects,
target = target,
callback = callback
}
if lines[currentLine - 1] then
table.insert(lines[currentLine - 1].choices, choice)
print("DEBUG: Added choice successfully")
else
print("DEBUG: Warning - No previous line to attach choice to")
end
else
print("DEBUG: Failed to parse choice line:", line)
end
elseif line:match("^%[.*%]") then
currentScene = line:match("^%[(.*)%]")
scenes[currentScene] = currentLine
end
end
return lines, characters, scenes
end
function Parser.printDebugInfo(lines, characters)
print("Parsed Lines:")
for i, line in ipairs(lines) do
print(string.format("Line %d:", i))
print(string.format(" Character: %s", line.character))
print(string.format(" Text: %s", line.text))
print(string.format(" Is End: %s", tostring(line.isEnd)))
print(" Effects:")
for _, effect in ipairs(line.effects) do
print(string.format(" Type: %s", effect.type))
print(string.format(" Content: %s", effect.content))
print(string.format(" Start Index: %d", effect.startIndex))
print(string.format(" End Index: %d", effect.endIndex))
end
if line.branches then
print(" Branches:")
for branchIndex, branch in ipairs(line.branches) do
print(string.format(" Branch %d:", branchIndex))
print(string.format(" Target Line: %d", branch.targetLine))
print(string.format(" Text: %s", branch.text))
-- Check and print branch effects
if branch.effects then
print(" Effects:")
for _, effect in ipairs(branch.effects) do
print(string.format(" Type: %s", effect.type))
print(string.format(" Content: %s", effect.content))
print(string.format(" Start Index: %d", effect.startIndex))
print(string.format(" End Index: %d", effect.endIndex))
end
else
print(" Effects: None")
end
-- Print branch callback
if branch.callback then
print(" Callback: Loaded")
else
print(" Callback: Not loaded")
end
end
end
end
print("Characters:")
for character, color in pairs(characters) do
print(string.format(" Character: %s", character))
print(string.format(" Color: R=%f, G=%f, B=%f", color.r, color.g, color.b))
end
end
return Parser